kembali di tengah blok menggunakan

196

Sesuatu seperti:

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

Saya percaya itu bukan tempat yang tepat untuk pernyataan kembali, bukan?

tafa
sumber

Jawaban:

194

Seperti yang ditunjukkan beberapa orang secara umum, ini bukan masalah.

Satu-satunya kasus itu akan menyebabkan Anda masalah adalah jika Anda kembali di tengah pernyataan menggunakan dan juga mengembalikan variabel menggunakan. Tetapi sekali lagi, ini juga akan menyebabkan Anda masalah bahkan jika Anda tidak kembali dan hanya menyimpan referensi ke variabel.

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

Sama buruknya

Something y;
using ( var x = new Something() ) {
  y = x;
}
JaredPar
sumber
1
Baru saja saya hendak mengedit pertanyaan saya tentang poin yang Anda sebutkan. Terima kasih.
tafa
Tolong bantu saya memahami mengapa ini buruk. Saya ingin mengembalikan Stream yang saya gunakan dalam fungsi pembantu ke fungsi lain untuk pemrosesan gambar. Sepertinya Stream akan dibuang jika saya melakukan ini?
John Shedletsky
3
@ JohnShedletsky Dalam hal ini panggilan fungsi Anda harus dibungkus dengan menggunakan. Seperti menggunakan (Streaming x = FuncToReturnStream ()) {...} dan tidak menggunakan FuncToReturnStream.
Felix Keil
@ JohnShedletsky Saya yakin itu karena returnpernyataan membuat akhir usingblok tidak dapat diakses oleh jalur kode apa pun. Ujung usingblok perlu dijalankan sehingga objek dapat dibuang jika diperlukan.
facepalm42
147

Tidak apa-apa.

Anda tampaknya memikirkan itu

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

diterjemahkan secara membabi buta ke:

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

Yang, diakui, akan menjadi masalah, dan akan membuat usingpernyataan itu agak sia-sia --- itulah sebabnya bukan itu yang dilakukannya.

Compiler memastikan bahwa objek dibuang sebelum kontrol meninggalkan blok - terlepas dari bagaimana ia meninggalkan blok.

James Curran
sumber
7
Rupanya saya.
tafa
Jawaban yang bagus @James Curran! Tapi itu membuat saya agak ingin tahu apa yang diterjemahkan. Atau hanya ekspresible di IL? (yang saya belum pernah benar-benar mencoba membaca sebelumnya).
Bart
1
@ Bart - Saya menganggapnya sebagai mengevaluasi ekspresi kembali ke variabel sementara, kemudian melakukan buang, lalu mengembalikan variabel sementara.
ToolmakerSteve
@ James Curran. Dari atas ke sini, Hanya Anda yang menjelaskan apa yang terjadi di latar belakang. Terimakasih banyak.
Sercan Timoçin
@Bart mungkin diterjemahkan ke: coba {... kode Anda ...} akhirnya {x.Dispose (); }
Bip901
94

Tidak apa-apa - tidak ada masalah sama sekali. Mengapa Anda percaya itu salah?

Pernyataan menggunakan hanyalah gula sintaksis untuk blok percobaan / akhirnya, dan seperti yang dikatakan Grzenio, tidak apa-apa untuk kembali dari blok percobaan juga.

Ekspresi pengembalian akan dievaluasi, maka blok akhirnya akan dieksekusi, maka metode akan kembali.

Jon Skeet
sumber
5
Jawaban James Curran menjelaskan apa yang saya pikirkan.
tafa
27

Ini akan bekerja dengan sangat baik, sama seperti kembali di tengah try{}finally{}

Grzenio
sumber
18

Itu benar-benar dapat diterima. Sebuah menggunakan pernyataan memastikan obyek IDisposable akan dibuang tidak peduli apa.

Dari MSDN :

Pernyataan menggunakan memastikan Buang dipanggil bahkan jika pengecualian terjadi saat Anda memanggil metode pada objek. Anda dapat mencapai hasil yang sama dengan meletakkan objek di dalam blok coba lalu memanggil Buang di akhirnya blok; sebenarnya, ini adalah bagaimana pernyataan penggunaan diterjemahkan oleh kompiler.

mbillard
sumber
14

Kode di bawah ini menunjukkan cara usingkerjanya:

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());
}

Keluaran:

't1' dibuat.
't2' dibuat.
't3' dibuat.
'Dibuat dari t1, t2, t3' dibuat.
't3' dibuang.
't2' dibuang.
't1' dibuang.

Yang dibuang dipanggil setelah pernyataan kembali tetapi sebelum keluar dari fungsi.

Bertrand
sumber
1
Harap dicatat bahwa beberapa objek C # dibuang dengan cara kustom, misalnya, klien WCF adalah pernyataan menggunakan seperti pengembalian di atas "tidak dapat mengakses objek yang dibuang"
OzBob
-4

Mungkin tidak 100% benar bahwa ini dapat diterima ...

Jika Anda kebetulan menggunakan sarang dan kembali dari sarang, mungkin itu tidak aman.

Ambil ini sebagai contoh:

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();
        }
    }
}

Saya melewati DataTable untuk di-output sebagai csv. Dengan kembalinya di tengah, itu menulis semua baris ke aliran, tetapi csv yang dihasilkan selalu hilang satu baris (atau beberapa, tergantung pada ukuran buffer). Ini memberi tahu saya bahwa ada sesuatu yang tidak ditutup dengan benar.

Cara yang benar adalah memastikan semua penggunaan sebelumnya dibuang dengan benar:

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();
}
ya benar
sumber