PEMBARUAN : Saya menggunakan pertanyaan ini sebagai dasar untuk artikel yang dapat ditemukan di sini ; lihat untuk diskusi tambahan tentang masalah ini. Terima kasih atas pertanyaan yang bagus!
Meskipun jawaban Schabse tentu saja benar dan menjawab pertanyaan yang diajukan, ada varian penting dari pertanyaan Anda yang tidak Anda ajukan:
Apa yang terjadi jika font4 = new Font()
lemparan setelah sumber daya yang tidak dikelola dialokasikan oleh konstruktor tetapi sebelum ctor kembali dan diisi font4
dengan referensi?
Biarkan saya membuatnya sedikit lebih jelas. Misalkan kita memiliki:
public sealed class Foo : IDisposable
{
private int handle = 0;
private bool disposed = false;
public Foo()
{
Blah1();
int x = AllocateResource();
Blah2();
this.handle = x;
Blah3();
}
~Foo()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!this.disposed)
{
if (this.handle != 0)
DeallocateResource(this.handle);
this.handle = 0;
this.disposed = true;
}
}
}
Sekarang kita punya
using(Foo foo = new Foo())
Whatever(foo);
Ini sama dengan
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
BAIK. Misalkan Whatever
melempar. Kemudianfinally
blok tersebut berjalan dan sumber daya dibatalkan alokasinya. Tidak masalah.
Misalkan Blah1()
melempar. Kemudian lemparan terjadi sebelum sumber daya dialokasikan. Objek telah dialokasikan tetapi ctor tidak pernah kembali, jadi foo
tidak pernah diisi. Kami tidak pernah masuk try
sehingga kami tidak pernah memasukkan finally
keduanya. Referensi objek telah menjadi yatim piatu. Akhirnya GC akan menemukannya dan meletakkannya di antrean finalizer. handle
masih nol, jadi finalizer tidak melakukan apa pun. Perhatikan bahwa finalizer harus kuat dalam menghadapi objek yang sedang diselesaikan yang konstruktornya tidak pernah selesai . Anda dibutuhkan untuk menulis finalisator yang sekuat ini. Ini adalah alasan lain mengapa Anda harus menyerahkan tugas akhir penulisan kepada para ahli dan tidak mencoba melakukannya sendiri.
Misalkan Blah3()
melempar. Lemparan terjadi setelah sumber daya dialokasikan. Tapi sekali lagi, foo
tidak pernah diisi, kita tidak pernah masukfinally
, dan objek dibersihkan oleh thread finalizer. Kali ini pegangannya bukan nol, dan finalizer membersihkannya. Sekali lagi, finalizer berjalan pada objek yang konstruktornya tidak pernah berhasil, tetapi finalizer tetap berjalan. Jelas harus karena kali ini, ada pekerjaan yang harus dilakukan.
Sekarang misalkan Blah2()
lemparan. Lemparan terjadi setelah sumber daya dialokasikan tetapi sebelum handle
diisi! Sekali lagi, finalizer akan berjalan tetapi sekarang handle
masih nol dan kami membocorkan pegangannya!
Anda perlu menulis kode yang sangat pintar untuk mencegah kebocoran ini terjadi. Sekarang, dalam kasus Font
sumber daya Anda , siapa yang peduli? Kami membocorkan pegangan font, masalah besar. Tetapi jika Anda benar-benar positif memerlukan bahwa setiap sumber daya unmanaged dibersihkan tidak peduli apa waktu pengecualian adalah maka Anda memiliki masalah yang sangat sulit di tangan Anda.
CLR harus menyelesaikan masalah ini dengan kunci. Sejak C # 4, kunci yang menggunakan lock
pernyataan tersebut telah diimplementasikan seperti ini:
bool lockEntered = false;
object lockObject = whatever;
try
{
Monitor.Enter(lockObject, ref lockEntered);
lock body here
}
finally
{
if (lockEntered) Monitor.Exit(lockObject);
}
Enter
telah ditulis dengan sangat hati-hati sehingga apa pun pengecualian yang dilemparkan , lockEntered
disetel ke true jika dan hanya jika kunci benar-benar diambil. Jika Anda memiliki persyaratan serupa maka yang perlu Anda lakukan sebenarnya adalah menulis:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
dan tulis AllocateResource
dengan cerdik Monitor.Enter
sehingga apa pun yang terjadi di dalamnya AllocateResource
, handle
isinya jika dan hanya jika perlu dibatalkan alokasinya.
Menjelaskan teknik untuk melakukannya berada di luar cakupan jawaban ini. Konsultasikan dengan ahlinya jika Anda memiliki persyaratan ini.