'ใช้' เหมาะสมหรือไม่ในบริบทที่ไม่มีอะไรทิ้ง?


9

ใน C # usingคำสั่งจะถูกใช้เพื่อกำจัดทรัพยากรอย่างไม่ถูกต้องโดยไม่ต้องรอการเก็บขยะ ตัวอย่างเช่นอาจใช้เพื่อ:

  • กำจัดคำสั่งหรือการเชื่อมต่อ SQL

  • ปิดสตรีมปล่อยแหล่งข้อมูลอ้างอิงเช่นไฟล์

  • องค์ประกอบ GDI + ฟรี

  • เป็นต้น

ฉันสังเกตเห็นว่าusingมีการใช้งานมากขึ้นในกรณีที่ไม่มีสิ่งใดที่จะกำจัด แต่ที่มันสะดวกกว่าสำหรับผู้โทรที่จะเขียนusingบล็อกแทนที่จะเป็นสองคำสั่งแยกกัน

ตัวอย่าง:

  • MiniProfilerเขียนโดยทีม Stack Overflow ใช้usingเพื่อแสดงถึงบล็อกในโปรไฟล์:

    using (profiler.Step("Name goes here"))
    {
        this.DoSomethingUseful(i - 1);
    }
    

    ทางเลือกหนึ่งที่จะมีสองช่วงตึก:

    var p = profiler.Start("Name goes here");
    this.DoSomethingUseful(i - 1);
    profiler.Stop(p);
    

    อีกวิธีหนึ่งคือการใช้การกระทำ:

    profiler.Step("Name goes here", () => this.DoSomethingUseful(i - 1));
  • ASP.NET MVC ยังเลือกusingสำหรับแบบฟอร์ม:

    <% using (Html.BeginForm())
       { %>
           <label for="firstName">Name:</label>
           <%= Html.TextBox("name")%>
           <input type="submit" value="Save" />    
    <% } %>
    

การใช้งานดังกล่าวเหมาะสมหรือไม่? วิธีการพิสูจน์ความถูกต้องเนื่องจากมีข้อบกพร่องหลายประการ:

  • ผู้เริ่มต้นจะหายไปเนื่องจากการใช้งานดังกล่าวไม่ตรงกับที่อธิบายไว้ในหนังสือและข้อกำหนดทางภาษา

  • รหัสควรแสดงออก ที่นี่การแสดงออกที่ได้รับความเดือดร้อนเนื่องจากการใช้งานที่เหมาะสมของusingการแสดงให้เห็นว่าข้างหลังมีทรัพยากรเช่นกระแสการเชื่อมต่อเครือข่ายหรือฐานข้อมูลที่ควรได้รับการปล่อยตัวโดยไม่ต้องรอเก็บขยะ


2
วิธีการที่มีสองข้อความส่วนตัวได้รับความเดือดร้อนจากปัญหาเดียวกันกับการจัดการทรัพยากรไร้เดียงสาที่ไม่มีusing: คำสั่งหลัง (เช่นprofiler.Stop(p)) ไม่รับประกันว่าจะถูกดำเนินการเมื่อเผชิญกับข้อยกเว้นและโฟลว์การควบคุม

1
@delnan: นี่อธิบายว่าทำไม MiniProfiler หลีกเลี่ยงวิธีที่สอง แต่สิ่งที่สองควรหลีกเลี่ยงในทุกกรณีเนื่องจากเป็นการยากที่จะเขียนและบำรุงรักษา
Arseni Mourzenko

1
ที่เกี่ยวข้อง: stackoverflow.com/questions/9472304
Robert Harvey

1
ที่เกี่ยวข้อง: stackoverflow.com/questions/2101524และstackoverflow.com/questions/1095438
Robert Harvey

1
เกี่ยวข้อง: grabbagoft.blogspot.com/2007/06/…
Robert Harvey

คำตอบ:


7

ข้อความสุดท้ายของคุณ - ว่า "การใช้งานที่เหมาะสมในการใช้งานคือการแสดงว่ามีทรัพยากรเช่นสตรีมการเชื่อมต่อเครือข่ายหรือฐานข้อมูลที่ควรเผยแพร่โดยไม่ต้องรอตัวรวบรวมขยะ" ไม่ถูกต้องและสาเหตุที่เป็น ให้ไว้ที่เอกสารประกอบสำหรับส่วนต่อประสาน IDisposable: http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

การใช้งานหลักของอินเทอร์เฟซนี้คือการปล่อยทรัพยากรที่ไม่มีการจัดการ

ดังนั้นถ้าคลาสของคุณใช้ทรัพยากรที่ไม่มีการจัดการมันไม่สำคัญว่าคุณจะหรืออาจไม่ต้องการให้ GC เกิดขึ้น - มันไม่มีอะไรเกี่ยวข้องกับ GC เพราะทรัพยากรที่ไม่มีการจัดการนั้นไม่ใช่ GC'ed ( https: // stackoverflow com / คำถาม / 3607213 / สิ่งที่มีความหมายโดยจัดการกับทรัพยากรที่ไม่มีการจัดการในสุทธิ )

ดังนั้นจุดประสงค์ของการ "ใช้งาน" นั้นก็เพื่อหลีกเลี่ยงการรอคอย GC มันเป็นการบังคับให้ปล่อยทรัพยากรที่ไม่มีการจัดการตอนนี้ก่อนที่อินสแตนซ์ของคลาสจะออกนอกขอบเขตและมันถูกเรียกว่า Finalize นี่เป็นสิ่งสำคัญสำหรับเหตุผลที่ควรชัดเจน - ทรัพยากรที่ไม่มีการจัดการอาจมีการพึ่งพาทรัพยากรที่ไม่มีการจัดการอื่น ๆ และหากมีการกำจัด (หรือสรุป) ในลำดับที่ไม่ถูกต้องสิ่งเลวร้ายอาจเกิดขึ้น

ดังนั้นถ้าคลาสที่ถูกอินสแตนซ์ในบล็อกการใช้ใช้ทรัพยากรที่ไม่มีการจัดการใด ๆ ดังนั้นคำตอบคือใช่ - มันเหมาะสม

โปรดทราบว่า IDisposable ไม่ได้กำหนดไว้ล่วงหน้าว่าเป็นเพียงการปล่อยทรัพยากรที่ไม่มีการจัดการอย่างไรก็ตาม - เพียงแค่ว่านี่เป็นวัตถุประสงค์หลัก มันอาจจะเป็นกรณีที่ผู้เขียนของชั้นที่มีการกระทำบางอย่างที่พวกเขาต้องการที่จะบังคับใช้เกิดขึ้นในเวลาที่แน่นอนและการใช้ IDisposable อาจจะมีวิธีในการได้รับที่จะเกิดขึ้น แต่ไม่ว่าจะได้หรือไม่ว่าเป็นทางออกที่สวยงามเป็นสิ่งที่สามารถ ตอบเฉพาะในแต่ละกรณี

ไม่ว่าการใช้ "การใช้" หมายความว่าคลาสใช้ IDisposable ดังนั้นจึงไม่ละเมิดการแสดงออกของโค้ด มันทำให้ค่อนข้างชัดเจนว่าเกิดอะไรขึ้นในความเป็นจริง


"การใช้งานหลักของอินเทอร์เฟซนี้คือการปล่อยทรัพยากรที่ไม่มีการจัดการ" ฉันว่าเอกสารของ Microsoft sucks ในจุดนั้น บางทีนั่นอาจเป็นวิธีที่พวกเขาใช้ใน FCL แต่นักพัฒนาแอปพลิเคชันได้ใช้มันเพื่อสิ่งต่าง ๆ มาเกือบทศวรรษแล้ว - บางคนก็มีเหตุผลที่ดีบางคนก็มีเหตุผลที่ไม่ดี แต่การอ้างว่าเอกสารของพวกเขาไม่ตอบคำถามของ OP เลย
Jesse C. Slicer

1
สังเกต para ครั้งที่สองของฉัน - วัตถุประสงค์หลักไม่จำเป็นต้องมีวัตถุประสงค์เพียงอย่างเดียว IDisposable สามารถนำไปใช้ได้ไม่ว่าด้วยเหตุผลใด ๆ และยังมีอินเตอร์เฟสที่รู้จักพร้อมกับพฤติกรรมที่คาดหวังและรูปแบบการใช้งานดังนั้นเมื่อคุณเห็นมันคุณรู้ว่ามีสิ่งอื่นที่ต้องเกิดขึ้นก่อนที่อินสแตนซ์จะปล่อยให้พ้นขอบเขต ประเด็นคือ: ถ้ามันใช้ IDisposable การใช้งานก็เหมาะสม ทุกสิ่งทุกอย่างเป็นเพียงคำนำ
Maximus Minimus

1
โดยทั่วไปคุณกำลังบอกว่าจุดประสงค์คือการหลีกเลี่ยงการรอ GC แต่ก็หลีกเลี่ยงการรอรอบสุดท้าย แต่ฉันไม่เห็นความแตกต่าง: นักเรียกสุดท้ายคือ GC
svick

GC ทำงานในลักษณะที่ไม่สามารถกำหนดได้ - คุณไม่รู้ว่าจะเรียกใช้เมื่อใด GC ก็แค่เรียกเครื่องมือสุดท้าย แต่มันก็ไม่ได้ทำการกำจัดเองทรัพยากรที่แท้จริงนั้นอยู่นอกเหนือการควบคุมของ GC ด้วยการใช้ (1) วัตถุนั้นถูกกำหนดขอบเขตอย่างเหมาะสมและ (2) การกำจัดเกิดขึ้น ณ จุดที่ทราบ - เมื่อมันออกจากขอบเขตของบล็อกที่ใช้ทรัพยากรที่ไม่มีการจัดการจะหายไป (หรือ - กรณีที่เลวร้ายที่สุด - มือ ไปที่อย่างอื่นที่จะทำลายมัน) จริงๆแล้วนี่เป็นเพียงการสร้างปัญหา อ่านเอกสารทั้งหมดนี้ไม่จำเป็นต้องทำซ้ำ
Maximus Minimus

8

การใช้usingหมายถึงการมีอยู่ของDispose()วิธีการ โปรแกรมเมอร์อื่น ๆ จะสันนิษฐานว่าวิธีการดังกล่าวมีอยู่ในวัตถุ ดังนั้นหากวัตถุไม่ได้ถูกทิ้งคุณไม่ควรใช้usingมัน

ความชัดเจนของรหัสคือราชา ละเว้นusingหรือนำไปใช้IDisposableกับวัตถุ

MiniProfiler ดูเหมือนจะใช้usingเป็นกลไกในการ "ล้อมรั้ว" รหัสที่ถูกทำโปรไฟล์ มีบุญอยู่บ้าง สันนิษฐานว่า MiniProfiler กำลังเรียกDispose()ให้หยุดตัวจับเวลาหรือตัวจับเวลาจะหยุดลงเมื่อวัตถุ MiniProfiler ไม่อยู่ในขอบเขต

โดยทั่วไปคุณจะเรียกใช้usingเมื่อการสรุปบางประเภทต้องเกิดขึ้นโดยอัตโนมัติ เอกสารสำหรับhtml.BeginFormระบุว่าเมื่อใช้เมธอดในusingคำสั่งจะแสดงผล</form>แท็กปิดที่ส่วนท้ายของusingบล็อก

ไม่ได้แปลว่ามันยังไม่เป็นการละเมิดอยู่ดี


4
จนถึงตอนนี้ชัดเจน คำถามคือเมื่อ (ถ้าทั้งหมด) มันเหมาะสมที่จะใช้IDisposable/ Dispose()แม้จะไม่มีอะไรที่จะกำจัดในความหมาย OP อธิบาย?

2
ฉันคิดว่าฉันทำให้ชัดเจน; มีไม่ได้เป็นเวลาที่เหมาะสม
Robert Harvey

1
ประเด็นของฉันคือDisposeเป็นสิ่งจำเป็นสำหรับusingการทำความเข้าใจทั้งหมด (ไม่ว่าจะเหมาะสมหรือไม่) ตัวอย่างของ OP ใช้ทั้งหมดDisposeหรือไม่ และ IIUC คำถามคือว่าถูกต้องหรือไม่ที่คลาสเหล่านี้ใช้Disposeมากกว่าวิธีอื่น (เช่นStop()MiniProfiler)

1
ใช่ แต่นั่นไม่ใช่คำถาม คำถามคือว่าควรทำเช่นนั้นหรือไม่

2
@delnan: อีกครั้งฉันคิดว่าฉันทำให้ชัดเจน ถ้ามันทำให้เจตนาของโค้ดของคุณชัดเจนขึ้นมันเป็นความคิดที่ดี หากไม่เป็นเช่นนั้น
Robert Harvey

1

usingเป็นข้อผิดพลาดและข้อยกเว้นที่ปลอดภัย มันDispose()จะมั่นใจได้ว่าจะถูกเรียกโดยไม่คำนึงถึงความผิดพลาดใด ๆ ที่โปรแกรมเมอร์ทำ มันไม่ได้ยุ่งเกี่ยวกับการจับหรือการจัดการของข้อยกเว้น แต่Dispose()วิธีการจะดำเนินการขึ้นสแต็กซ้ำเมื่อโยนข้อยกเว้น

วัตถุที่นำไปใช้IDisposeแต่ไม่มีอะไรจะกำจัดเมื่อทำเสร็จแล้ว ดีที่สุดพิสูจน์การออกแบบในอนาคต เพื่อที่คุณจะได้ไม่ต้อง refactor ซอร์สโค้ดของคุณเมื่อในอนาคตพวกเขาจำเป็นต้องกำจัดบางอย่าง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.