กลับมาอยู่ตรงกลางของบล็อกที่ใช้


196

สิ่งที่ต้องการ:

using (IDisposable disposable = GetSomeDisposable())
{
    //.....
    //......
    return Stg();
}

ฉันเชื่อว่ามันไม่ใช่สถานที่ที่เหมาะสมสำหรับการส่งคืนสินค้าใช่หรือไม่

คำตอบ:


194

ตามที่คนอื่นหลายคนชี้ให้เห็นโดยทั่วไปแล้วนี่ไม่ใช่ปัญหา

กรณีเดียวที่จะทำให้คุณมีปัญหาคือถ้าคุณกลับมาที่ตรงกลางของคำสั่งการใช้และส่งกลับในการใช้ตัวแปร แต่จากนั้นอีกครั้งสิ่งนี้จะทำให้คุณมีปัญหาแม้ว่าคุณจะไม่ได้กลับมาและเพียงแค่อ้างอิงถึงตัวแปร

using ( var x = new Something() ) { 
  // not a good idea
  return x;
}

เช่นเดียวกับที่ไม่ดี

Something y;
using ( var x = new Something() ) {
  y = x;
}

1
ฉันเพิ่งจะแก้ไขคำถามของฉันเกี่ยวกับประเด็นที่คุณพูดถึง ขอบคุณ
tafa

โปรดช่วยฉันเข้าใจว่าทำไมสิ่งนี้ไม่ดี ฉันต้องการส่งกระแสข้อมูลที่ฉันใช้ในฟังก์ชั่นตัวช่วยกลับไปยังฟังก์ชันอื่นสำหรับการประมวลผลภาพ ดูเหมือนว่ากระแสจะถูกกำจัดถ้าฉันทำเช่นนี้?
John Shedletsky

3
@JohnShedletsky ในกรณีนี้การเรียกใช้ฟังก์ชันของคุณควรถูกห่อด้วยการใช้ กดไลค์โดยใช้ (สตรีม x = FuncToReturnStream ()) {... } และห้ามใช้ใน FuncToReturnStream
เฟลิกซ์ Keil

@JohnShedletsky ฉันแน่ใจว่าเป็นเพราะreturnคำสั่งทำให้จุดสิ้นสุดของusingบล็อกไม่สามารถเข้าถึงได้โดยเส้นทางรหัสใด ๆ จุดสิ้นสุดของusingบล็อกต้องถูกเรียกใช้เพื่อให้สามารถกำจัดวัตถุได้หากจำเป็น
facepalm42

147

มันดีมาก

เห็นได้ชัดว่าคุณกำลังคิดว่า

using (IDisposable disposable = GetSomeDisposable())
{
    //.....
    //......
    return Stg();
}

แปลแบบสุ่มสี่สุ่มห้าเป็น:

IDisposable disposable = GetSomeDisposable()
//.....
//......
return Stg();
disposable.Dispose();

ซึ่งเป็นที่ยอมรับจะมีปัญหาและจะทำให้usingคำสั่งค่อนข้างไม่มีจุดหมาย --- ซึ่งเป็นสาเหตุที่ไม่ใช่สิ่งที่มันทำ

คอมไพเลอร์ตรวจสอบให้แน่ใจว่าวัตถุถูกกำจัดก่อนการควบคุมออกจากบล็อก - ไม่ว่ามันจะออกจากบล็อกอย่างไร


7
เห็นได้ชัดว่าฉัน
tafa

คำตอบยอดเยี่ยม @James Curran! แต่มันทำให้ฉันค่อนข้างอยากรู้ว่ามันแปลเป็นอะไร หรือว่าเฉพาะใน IL? (ซึ่งฉันไม่เคยลองอ่านมาก่อน)
บาร์ต

1
@Bart - ฉันคิดว่ามันเป็นการประเมินผลการแสดงออกกลับเป็นตัวแปรชั่วคราวแล้วทำการกำจัดแล้วกลับตัวแปรชั่วคราว
ToolmakerSteve

@James Curran จากด้านบนถึงที่นี่มีเพียงคุณเท่านั้นที่อธิบายสิ่งที่เกิดขึ้นในพื้นหลัง ขอบคุณมาก.
Sercan Timoçin

@Bart อาจแปลเป็น: ลอง {... รหัสของคุณ ... } ในที่สุด {x.Dispose (); }
Bip901

94

ไม่เป็นปัญหาเลย ทำไมคุณถึงเชื่อว่ามันผิด

การใช้ข้อความเป็นเพียงน้ำตาลเชิงประโยคสำหรับการลอง / สุดท้ายบล็อกและในขณะที่ Grzenio บอกว่ามันดีที่จะกลับมาจากการลองอีกด้วย

นิพจน์ส่งคืนจะถูกประเมินจากนั้นบล็อกสุดท้ายจะถูกดำเนินการจากนั้นวิธีจะส่งคืน


5
คำตอบของ James Curran อธิบายสิ่งที่ฉันคิด
tafa

27

สิ่งนี้จะทำงานได้อย่างสมบูรณ์แบบเช่นเดียวกับการกลับมาอยู่ตรงกลาง try{}finally{}


18

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

จากMSDN :

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


14

รหัสตะโกนแสดงวิธีการusingทำงาน:

private class TestClass : IDisposable
{
   private readonly string id;

   public TestClass(string id)
   {
      Console.WriteLine("'{0}' is created.", id);
      this.id = id;
   }

   public void Dispose()
   {
      Console.WriteLine("'{0}' is disposed.", id);
   }

   public override string ToString()
   {
      return id;
   }
}

private static TestClass TestUsingClose()
{
   using (var t1 = new TestClass("t1"))
   {
      using (var t2 = new TestClass("t2"))
      {
         using (var t3 = new TestClass("t3"))
         {
            return new TestClass(String.Format("Created from {0}, {1}, {2}", t1, t2, t3));
         }
      }
   }
}

[TestMethod]
public void Test()
{
   Assert.AreEqual("Created from t1, t2, t3", TestUsingClose().ToString());
}

เอาท์พุท:

สร้าง 't1' แล้ว
สร้าง 't2' แล้ว
สร้าง 't3' แล้ว
สร้าง 'จาก t1, t2, t3' แล้ว
't3' ถูกกำจัด
't2' ถูกกำจัด
't1' ถูกกำจัด

การจำหน่ายจะถูกเรียกหลังจากคำสั่งส่งคืน แต่ก่อนที่จะออกจากฟังก์ชั่น


1
โปรดทราบว่าวัตถุ C # บางอย่างจะถูกกำจัดในรูปแบบที่กำหนดเองตัวอย่างเช่นลูกค้า WCF เป็นคำสั่งที่ใช้เช่นการส่งคืน "ไม่สามารถเข้าถึงวัตถุที่ถูกกำจัด"
OzBob

-4

บางทีมันอาจจะไม่จริง 100% ว่านี่เป็นที่ยอมรับ ...

หากคุณกำลังใช้การซ้อนและกลับมาจากภายในที่ซ้อนกันมันอาจไม่ปลอดภัย

ใช้สิ่งนี้เป็นตัวอย่าง:

using (var memoryStream = new MemoryStream())
{
    using (var textwriter = new StreamWriter(memoryStream))
    {
        using (var csv = new CsvWriter(textwriter))
        {
            //..write some stuff to the stream using the CsvWriter
            return memoryStream.ToArray();
        }
    }
}

ฉันผ่าน DataTable เพื่อเอาท์พุทเป็น csv ด้วยการส่งคืนตรงกลางมันกำลังเขียนแถวทั้งหมดไปยังสตรีม แต่เอาต์พุต csv หายไปเสมอแถว (หรือหลายขึ้นอยู่กับขนาดของบัฟเฟอร์) สิ่งนี้บอกฉันว่ามีบางอย่างไม่ได้ปิดอย่างถูกต้อง

วิธีที่ถูกต้องคือทำให้แน่ใจว่าการกำจัดการใช้ก่อนหน้านี้ทั้งหมดถูกต้อง:

using (var memoryStream = new MemoryStream())
{
    using (var textwriter = new StreamWriter(memoryStream))
    {
        using (var csv = new CsvWriter(textwriter))
        {
            //..write some stuff to the stream using the CsvWriter
        }
    }

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