GÜNCELLEME : Ben bulunabilir bir makale için temel olarak bu soruyu kullanılan burada ; bu konuyla ilgili ek tartışma için buna bakın. Güzel soru için teşekkürler!
Schabse'nin cevabı elbette doğru olsa ve sorulan soruyu cevaplasa da, sorunuzda sormadığınız önemli bir varyant var:
Yönetilmeyen kaynak kurucu tarafından tahsis edildikten sonra , ancak ctor geri dönüp referansla doldurmadan öncefont4 = new Font()
atışlar ne olur ?font4
Bunu biraz daha netleştireyim. Varsayalım ki:
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;
}
}
}
Şimdi sahibiz
using(Foo foo = new Foo())
Whatever(foo);
Bu aynıdır
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
TAMAM. Atışları varsayalım Whatever
. Daha sonra finally
blok çalışır ve kaynak serbest bırakılır. Sorun değil.
Atışları varsayalım Blah1()
. Ardından, kaynak ayrılmadan önce atış gerçekleşir. Nesne tahsis edilmiştir, ancak ctor asla geri dönmez, bu yüzden foo
asla doldurulmaz. Asla girmedik, try
bu yüzden finally
ikisine de girmeyiz. Nesne referansı artık kaldı. Sonunda GC bunu keşfedecek ve onu sonlandırıcı sırasına koyacaktır. handle
hala sıfırdır, bu nedenle sonlandırıcı hiçbir şey yapmaz. Sonlandırıcının, kurucusu hiçbir zaman tamamlanmayan, sonuçlandırılan bir nesne karşısında sağlam olması gerektiğine dikkat edin . Sen edilir gerekli bu güçlü finalizers yazmak için. Bu, son yazıyı uzmanlara bırakmanız ve bunu kendiniz yapmaya çalışmamanız için bir başka nedendir.
Atışları varsayalım Blah3()
. Kaynak tahsis edildikten sonra atış gerçekleşir. Ama yine, foo
asla doldurulmaz, asla girilmez finally
ve nesne sonlandırıcı ipliği tarafından temizlenir. Bu sefer tutamaç sıfır değildir ve sonlandırıcı onu temizler. Yine, sonlandırıcı, kurucusu asla başarılı olamayan bir nesne üzerinde çalışıyor, ancak sonlandırıcı yine de çalışıyor. Açıkçası olmalı çünkü bu sefer yapacak işleri vardı.
Şimdi Blah2()
atışları varsayalım . Atış, kaynak tahsis edildikten sonra ancak doldurulmadan önce handle
gerçekleşir! Yine, sonlandırıcı çalışacak ama şimdi handle
hala sıfır ve kolu sızdırıyoruz!
Bu sızıntının olmasını önlemek için son derece akıllı bir kod yazmanız gerekir . Şimdi, Font
kaynağınız söz konusu olduğunda , kimin umurunda? Bir yazı tipi tanıtıcısı sızdırıyoruz, önemli. Ama eğer kesinlikle olumlu gerektiren bu her yönetilmeyen kaynak temizlenecek olursa olsun istisna zamanlaması ne o zaman ellerini çok zor bir sorun var.
CLR bu sorunu kilitlerle çözmelidir. C # 4'ten beri, lock
ifadeyi kullanan kilitler şu şekilde uygulanmıştır:
bool lockEntered = false;
object lockObject = whatever;
try
{
Monitor.Enter(lockObject, ref lockEntered);
lock body here
}
finally
{
if (lockEntered) Monitor.Exit(lockObject);
}
Enter
çok dikkatli bir şekilde yazılmıştır, böylece hangi istisnalar yapılırsa yapılsın , ancak ve ancak kilit gerçekten alınmışsa lockEntered
doğru olarak ayarlanır . Benzer gereksinimleriniz varsa, yapmanız gereken şey aslında yazmaktır:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
ve AllocateResource
zekice yazın Monitor.Enter
ki içeride ne olursa olsun AllocateResource
, sadece ve ancak ayrılması gerektiğinde handle
doldurulur .
Bunu yapmak için gereken teknikleri açıklamak bu cevabın kapsamı dışındadır. Bu gereksiniminiz varsa bir uzmana danışın.