จะเกิดอะไรขึ้นถ้าฉันกลับมาก่อนสิ้นสุดการใช้งานคำสั่ง? จะเรียกขายทิ้งหรือไม่


115

ฉันมีรหัสต่อไปนี้

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

dispose()วิธีการที่เรียกว่าในตอนท้ายของusingคำสั่งวงเล็บ}ใช่มั้ย? เนื่องจากฉัน returnก่อนสิ้นสุดusingคำสั่งMemoryStreamวัตถุจะถูกกำจัดอย่างถูกต้องหรือไม่? เกิดอะไรขึ้นที่นี่?


4
@ จอนห์: ค้นหารายการที่ซ้ำกันแล้วกรุณาโหวตเพื่อปิดในกรณีนั้น
Noldorin

@ โนลโดริน: ฉันไปหาคนหลอกลวงเรื่องนี้เพราะฉันคิดว่ามันต้องถูกถามมาก่อน แต่ฉันไม่พบ ฉันเดาว่ายังมีคำถามง่ายๆอยู่ที่นั่น :)
Randolpho

@JonH และ @Noldorin - จะมีการนำเสนอรายการที่ซ้ำกันเมื่อเกิดคำถามขึ้นค้นหา "คำถามที่คล้ายกัน" ซึ่งเป็นคุณลักษณะที่ผู้คนใช้ไม่เพียงพอ
Adam Houldsworth

@Adam: ไปลองเอง คัดลอก / วางชื่อเรื่องและดูว่าระบบนำเสนอรายการซ้ำอะไรบ้าง ฉันจะให้คำแนะนำคุณ: คำตอบคือไม่มี หากคุณค้นหาผ่านการค้นหาของ Google หรือ SO ดูเหมือนว่าคำถามนี้ไม่เคยถูกถามมาก่อน
Randolpho

อ๊าป ... ฉันเอาคืน ฉันเพิ่งพบรายการใกล้เคียงที่ซ้ำกันหลังจากการค้นหาโดยเฉพาะ: stackoverflow.com/questions/2641692/…ตอนนี้คำถามถูกถามแตกต่างกันอย่างสิ้นเชิง แต่คำถามสุดท้ายค่อนข้างเหมือนกัน ฉันคิดว่าเราสามารถมองว่านี่เป็นการหลอกลวงได้
Randolpho

คำตอบ:


167

ใช่Disposeจะเรียกว่า เรียกว่าทันทีที่การดำเนินการออกจากขอบเขตของusingบล็อกไม่ว่าจะใช้วิธีใดในการออกจากบล็อกไม่ว่าจะเป็นการสิ้นสุดการดำเนินการของบล็อกreturnคำสั่งหรือข้อยกเว้น

ดังที่ @Noldorin ชี้ให้เห็นอย่างถูกต้องการใช้usingบล็อกในโค้ดจะถูกคอมไพล์เป็นtry/ finallyโดยDisposeถูกเรียกในfinallyบล็อก ตัวอย่างเช่นรหัสต่อไปนี้:

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

อย่างมีประสิทธิภาพกลายเป็น:

MemoryStream ms = new MemoryStream();
try
{
    // code
    return 0;
}
finally
{
    ms.Dispose();
}

ดังนั้นเนื่องจากfinallyมีการรับประกันว่าจะดำเนินการหลังจากtryบล็อกเสร็จสิ้นการดำเนินการโดยไม่คำนึงถึงเส้นทางการดำเนินการDisposeจึงรับประกันได้ว่าจะถูกเรียกไม่ว่าจะเกิดอะไรขึ้นก็ตาม

สำหรับข้อมูลเพิ่มเติมโปรดดูที่บทความนี้ MSDN

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


4
ฉันคิดว่าคุณจะพบว่ามันถูกรวบรวมอย่างมีประสิทธิภาพในการบล็อกในที่สุดพร้อมกับการโทรเข้าDisposeในที่สุดดังนั้นจึงสามารถใช้งานได้อย่างมีประสิทธิภาพfinallyตามที่คุณอธิบาย
Noldorin

@ โนลโดริน: เป๊ะ แม้ว่าฉันคิดว่าฉันอาจจะชัดเจนเกี่ยวกับเรื่องนี้ แก้ไขเตรียมพร้อม ....
Randolpho

1
นอกจากนี้โปรดทราบว่ามีบางสถานการณ์ที่ไม่รับประกันการบล็อกในที่สุดในการดำเนินการเช่นการใช้ Environment.FailFast และหากเกิด StackOverFlowException
Christopher McAtackney

@ C.McAtackney: ยังเป็นจุดที่ดี นอกจากนี้ IIRC, OutOfMemoryException; โดยพื้นฐานแล้วถ้าคุณไม่สามารถจับข้อยกเว้นได้เนื่องจากเป็นความล้มเหลวในการดำเนินการที่สำคัญ Dispose จะไม่ถูกเรียก แน่นอนว่าในกรณีนี้โปรแกรมรับประกันว่าจะขัดข้องพร้อมกับหน่วยความจำใด ๆ ที่จัดสรรให้ดังนั้นใน 99.9% ของกรณีนี้จะไม่ใช่ปัญหาเว้นแต่คุณจะทำสิ่งที่ไม่น่าสนใจเช่นการเขียนลงไฟล์ด้วยวิธีการกำจัดของคุณ . นอกเหนือจากความผิดพลาดของโปรแกรมหายนะนั่นคือ
Randolpho

คุณไม่ควรใช้คำสั่ง 'using ()' กับ WCF โปรดดูบทความนี้สำหรับข้อมูลเพิ่มเติม นี่คือตัวอย่างที่ฉันใช้สำหรับพร็อกซี WCF: 'WCFProxy variableName = null; ลองใช้ {variableName = new WCFProxy (); // รหัส TODO ที่นี่ variableName.Proxy.Close (); variableName.Dispose (); } catch (Exception) {if (variableName! = null && variableName.Proxy! = null) {variableName.Proxy.Abort (); } โยน; } '
Dave Black

18

usingคำสั่งจะทำงานเหมือนกับtry ... finallyบล็อกดังนั้นจะดำเนินการบนเส้นทางออกของรหัสเสมอ อย่างไรก็ตามฉันเชื่อว่าพวกเขาอยู่ภายใต้สถานการณ์น้อยมากและหายากมากที่finallyไม่มีการเรียกบล็อค ตัวอย่างหนึ่งที่ฉันจำได้คือหากเธรดเบื้องหน้าออกในขณะที่เธรดพื้นหลังทำงานอยู่เธรดทั้งหมดที่นอกเหนือจาก GC จะหยุดชั่วคราวหมายความว่าfinallyบล็อกจะไม่ทำงาน

การแก้ไขที่ชัดเจน:พวกมันทำงานเหมือนกันนอกเหนือจากตรรกะที่ช่วยให้พวกเขาจัดการกับวัตถุที่แยกได้ออกจากกันได้

เนื้อหาโบนัส:สามารถซ้อนกันได้ (โดยที่ประเภทต่างกัน):

using (SqlConnection conn = new SqlConnection("string"))
using (SqlCommand comm = new SqlCommand("", conn))
{

}

และคั่นด้วยเครื่องหมายจุลภาค (โดยที่ประเภทเหมือนกัน):

using (SqlCommand comm = new SqlCommand("", conn), 
       SqlCommand comm2 = new SqlCommand("", conn))
{

}

4

วัตถุ MemoryStream ของคุณจะถูกกำจัดอย่างเหมาะสมไม่ต้องกังวลเรื่องนั้น



0

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

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