Apakah 'menggunakan' sesuai dalam konteks di mana tidak ada yang dibuang?

9

Dalam C #, usingpernyataan digunakan untuk membuang sumber daya secara deterministik tanpa menunggu pemulung. Misalnya, ini dapat digunakan untuk:

  • Buang perintah atau koneksi SQL,

  • Tutup aliran, membebaskan sumber yang mendasarinya seperti file,

  • Elemen GDI + gratis,

  • dll.

Saya perhatikan bahwa usingsemakin sering digunakan dalam kasus-kasus di mana tidak ada yang dibuang, tetapi di mana lebih mudah bagi penelepon untuk menulis usingblok daripada dua perintah terpisah.

Contoh:

  • MiniProfiler , ditulis oleh tim Stack Overflow, digunakan usinguntuk menunjukkan blok ke profil:

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

    Satu pendekatan alternatif adalah memiliki dua blok:

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

    Pendekatan lain adalah menggunakan tindakan:

    profiler.Step("Name goes here", () => this.DoSomethingUseful(i - 1));
  • ASP.NET MVC juga memilih usingformulir:

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

Apakah penggunaan seperti itu tepat? Cara membenarkannya, mengingat bahwa ada beberapa kelemahan:

  • Pemula akan hilang, karena penggunaan tersebut tidak sesuai dengan yang dijelaskan dalam buku dan spesifikasi bahasa,

  • Kode harus ekspresif. Di sini, ekspresifitas menderita, karena penggunaan yang tepat usingadalah untuk menunjukkan bahwa di belakang, ada sumber daya, seperti aliran, koneksi jaringan atau database yang harus dirilis tanpa menunggu pengumpul sampah.

Arseni Mourzenko
sumber
2
Pendekatan memiliki dua pernyataan individual menderita masalah yang sama seperti manajemen sumber daya yang naif tanpa using: Pernyataan yang terakhir (misalnya profiler.Stop(p)) tidak dijamin akan dieksekusi dalam menghadapi pengecualian dan aliran kontrol.
1
@delnan: ini menjelaskan mengapa MiniProfiler menghindari pendekatan kedua. Tetapi yang kedua harus dihindari dalam semua kasus, karena sulit untuk menulis dan memelihara.
Arseni Mourzenko
1
Terkait: stackoverflow.com/questions/9472304
Robert Harvey
1
Terkait: grabbagoft.blogspot.com/2007/06/...
Robert Harvey

Jawaban:

7

Pernyataan terakhir Anda - bahwa "penggunaan penggunaan yang tepat adalah untuk menunjukkan bahwa di belakang, ada sumber daya, seperti aliran, koneksi jaringan atau database yang harus dirilis tanpa menunggu pengumpul sampah" salah, dan alasan mengapa diberikan pada dokumentasi untuk antarmuka IDisposable: http://msdn.microsoft.com/en-us/library/system.idisposable.aspx

Penggunaan utama antarmuka ini adalah untuk melepaskan sumber daya yang tidak dikelola.

Jadi, jika kelas Anda menggunakan sumber daya yang tidak dikelola, tidak masalah ketika Anda mungkin atau mungkin tidak ingin GC terjadi - itu tidak ada hubungannya dengan GC karena sumber daya yang tidak dikelola tidak GC'ed ( https: // stackoverflow. com / pertanyaan / 3607213 / apa-yang-dimaksud-oleh-dikelola-vs-tidak dikelola-sumber daya-in-net ).

Jadi tujuan "menggunakan" bukan untuk menghindari menunggu di GC, itu untuk memaksa rilis sumber daya yang tidak dikelola sekarang , sebelum instance kelas keluar dari ruang lingkup dan itu Finalisasi disebut. Ini penting untuk alasan yang harus jelas - sumber daya yang tidak dikelola mungkin memiliki ketergantungan pada sumber daya yang tidak dikelola lainnya, dan jika sumber daya tersebut dibuang (atau diselesaikan) dalam urutan yang salah, Hal Buruk dapat terjadi.

Jadi, jika kelas yang sedang diinvestasikan dalam blok menggunakan menggunakan sumber daya yang tidak dikelola, maka jawabannya adalah ya - itu tepat.

Perhatikan bahwa IDisposable tidak bersifat preskriptif tentang hal itu hanya untuk melepaskan sumber daya yang tidak dikelola, hanya saja ini tujuan utamanya . Ini mungkin menjadi kasus bahwa penulis kelas memiliki beberapa tindakan yang mereka ingin menegakkan terjadi pada waktu tertentu, dan menerapkan IDisposable mungkin cara untuk mendapatkan itu terjadi, tapi apakah atau tidak itu solusi elegan adalah sesuatu yang dapat hanya dijawab berdasarkan kasus per kasus.

Apapun, penggunaan "menggunakan" menyiratkan bahwa kelas mengimplementasikan IDisposable, sehingga tidak melanggar ekspresi kode; itu membuatnya sangat jelas apa yang sebenarnya terjadi.

Maximus Minimus
sumber
"Penggunaan utama antarmuka ini adalah untuk melepaskan sumber daya yang tidak dikelola." Saya katakan dokumentasi Microsoft menyebalkan pada titik itu. Mungkin itulah cara mereka menggunakannya di FCL, tetapi pengembang aplikasi telah menggunakannya untuk berbagai hal selama hampir satu dekade sekarang - beberapa untuk alasan yang baik, beberapa untuk alasan yang tidak begitu baik. Tetapi mengutip potongan dokumen mereka tidak menjawab pertanyaan OP sama sekali.
Jesse C. Slicer
1
Perhatikan paragraf terakhir kedua saya - tujuan utama tidak harus menjadi satu - satunya tujuan. IDisposable dapat diimplementasikan untuk alasan apa pun, dan menyediakan antarmuka yang dikenal dengan perilaku yang diharapkan dan gaya penggunaan, jadi ketika Anda melihatnya Anda tahu bahwa ada hal lain yang perlu terjadi sebelum instance dapat dilepaskan dari ruang lingkup. Intinya adalah: jika mengimplementasikan IDisposable, maka menggunakan adalah tepat; yang lainnya hanyalah mukadimah.
Maximus Minimus
1
Anda pada dasarnya mengatakan tujuannya bukan menghindari menunggu untuk GC, itu menghindari menunggu finalizer. Tapi saya tidak melihat perbedaannya: finalizer dipanggil oleh GC.
svick
GC beroperasi secara non-deterministik - Anda tidak tahu kapan akan dipanggil. GC juga hanya memanggil finalizer, tetapi tidak melakukan pembuangan sendiri - sumber daya yang sebenarnya itu sendiri berada di luar kendali GC. Dengan menggunakan (1) objek memiliki cakupan yang tepat, dan (2) pembuangan diketahui terjadi pada titik waktu yang diketahui - ketika keluar dari ruang lingkup blok penggunaan, sumber daya yang tidak dikelola hilang (atau - kasus terburuk - diserahkan ke sesuatu yang lain yang akan menghancurkannya). Sungguh, ini hanya nitpicking; baca dokumentasinya, itu semua yang seharusnya tidak perlu diulang.
Maximus Minimus
8

Penggunaan usingmenyiratkan adanya suatu Dispose()metode. Pemrogram lain akan menganggap bahwa metode semacam itu ada pada objek. Konsekuensinya, jika suatu objek tidak dapat dibuang, Anda sebaiknya tidak menggunakannya using.

Kejelasan kode adalah raja. Baik menghilangkan using, atau mengimplementasikan IDisposableobjek.

MiniProfiler tampaknya digunakan usingsebagai mekanisme untuk "memagari" kode yang diprofilkan. Ada beberapa manfaat untuk ini; mungkin, MiniProfiler sedang memanggil Dispose()untuk menghentikan timer, atau timer dihentikan ketika objek MiniProfiler keluar dari ruang lingkup.

Secara umum, Anda akan dipanggil usingketika semacam finalisasi perlu terjadi secara otomatis. Dokumentasi untuk html.BeginFormmenyatakan bahwa, ketika metode ini digunakan dalam usingpernyataan, itu membuat </form>tag penutup di akhir usingblok.

Itu tidak berarti itu masih bukan penyalahgunaan.

Robert Harvey
sumber
4
Sejauh ini sangat jelas. Pertanyaannya adalah, kapan (jika memang ada) yang tepat untuk diterapkan IDisposable/ Dispose()meskipun tidak memiliki apa pun untuk dibuang dalam arti yang digambarkan OP?
2
Saya pikir saya sudah menjelaskannya; ada tidak saat yang tepat.
Robert Harvey
1
Maksud saya adalah, Disposediperlukan untuk usingmasuk akal sama sekali (terlepas dari apakah itu sesuai). Semua contoh OP diterapkan Dispose, bukan? Dan IIUC, pertanyaannya adalah apakah benar bahwa kelas-kelas ini digunakan Dispose, daripada beberapa metode lain (seperti Stop()untuk MiniProfiler).
1
Ya, tapi bukan itu pertanyaannya. Pertanyaannya adalah apakah itu ide yang baik untuk melakukannya.
2
@ Darnan: Sekali lagi, saya pikir saya sudah menjelaskannya. Jika itu membuat maksud kode Anda lebih jelas, maka itu ide yang bagus. Jika tidak, itu tidak benar.
Robert Harvey
1

usingadalah kesalahan dan pengecualian aman. Itu memastikan Dispose()akan dipanggil terlepas dari kesalahan yang dibuat programmer. Ini tidak mengganggu penangkapan atau penanganan pengecualian, tetapi Dispose()metode ini dijalankan secara rekursif ke tumpukan ketika pengecualian dilemparkan.

Objek yang mengimplementasikan IDisposetetapi tidak memiliki apa pun untuk dibuang saat selesai. Paling-paling, masa depan membuktikan desain mereka. Sehingga Anda tidak perlu memperbaiki kode sumber Anda, ketika di masa depan, mereka perlu membuang sesuatu.

Reactgular
sumber