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 finallyblok ç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 fooasla doldurulmaz. Asla girmedik, trybu yüzden finallyikisine de girmeyiz. Nesne referansı artık kaldı. Sonunda GC bunu keşfedecek ve onu sonlandırıcı sırasına koyacaktır. handlehala 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, fooasla doldurulmaz, asla girilmez finallyve 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 handlehala 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, Fontkaynağı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, lockifadeyi 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 lockEntereddoğru olarak ayarlanır . Benzer gereksinimleriniz varsa, yapmanız gereken şey aslında yazmaktır:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
ve AllocateResourcezekice yazın Monitor.Enterki içeride ne olursa olsun AllocateResource, sadece ve ancak ayrılması gerektiğinde handledoldurulur .
Bunu yapmak için gereken teknikleri açıklamak bu cevabın kapsamı dışındadır. Bu gereksiniminiz varsa bir uzmana danışın.