AGGIORNAMENTO : ho usato questa domanda come base per un articolo che può essere trovato qui ; vederlo per ulteriori discussioni su questo problema. Grazie per la bella domanda!
Sebbene la risposta di Schabse sia ovviamente corretta e risponda alla domanda che è stata posta, c'è una variante importante della tua domanda che non hai posto:
Cosa succede se i font4 = new Font()
lanci dopo che la risorsa non gestita è stata allocata dal costruttore ma prima che il ctor ritorni e compili font4
il riferimento?
Permettimi di renderlo un po 'più chiaro. Supponiamo di avere:
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;
}
}
}
Ora abbiamo
using(Foo foo = new Foo())
Whatever(foo);
Questo è lo stesso di
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
OK. Supponiamo che Whatever
lanci. Quindi il finally
blocco viene eseguito e la risorsa viene deallocata. Nessun problema.
Supponiamo che Blah1()
lanci. Quindi il lancio avviene prima che la risorsa venga allocata. L'oggetto è stato assegnato ma il ctor non ritorna mai, quindi foo
non viene mai compilato. Non siamo mai entrati in try
quindi non entriamo mai in finally
nessuno dei due. Il riferimento all'oggetto è rimasto orfano. Alla fine il GC lo scoprirà e lo metterà nella coda del finalizzatore. handle
è ancora zero, quindi il finalizzatore non fa nulla. Si noti che il finalizzatore deve essere robusto di fronte a un oggetto che viene finalizzato il cui costruttore non è mai stato completato . È necessario scrivere finalizzatori così potenti. Questo è un altro motivo per cui dovresti lasciare i finalizzatori di scrittura agli esperti e non provare a farlo da solo.
Supponiamo che Blah3()
lanci. Il lancio avviene dopo che la risorsa è stata allocata. Ma ancora una volta, foo
non viene mai compilato, non entriamo mai in finally
e l'oggetto viene ripulito dal thread del finalizzatore. Questa volta la maniglia è diversa da zero e il finalizzatore la pulisce. Anche in questo caso, il finalizzatore è in esecuzione su un oggetto il cui costruttore non è mai riuscito, ma il finalizzatore viene eseguito comunque. Ovviamente doveva perché questa volta aveva del lavoro da fare.
Supponiamo ora che Blah2()
lanci. Il lancio avviene dopo che la risorsa è stata assegnata ma prima handle
è stata riempita! Ancora una volta, il finalizzatore verrà eseguito ma ora handle
è ancora zero e perdiamo la maniglia!
È necessario scrivere codice estremamente intelligente per evitare che si verifichi questa perdita. Ora, nel caso della tua Font
risorsa, a chi diavolo importa? Abbiamo perso un font handle, grosso problema. Ma se richiedi in modo assolutamente positivo che ogni risorsa non gestita venga ripulita, indipendentemente dalla tempistica delle eccezioni, allora hai un problema molto difficile tra le mani.
Il CLR deve risolvere questo problema con i blocchi. A partire da C # 4, i blocchi che utilizzano l' lock
istruzione sono stati implementati in questo modo:
bool lockEntered = false;
object lockObject = whatever;
try
{
Monitor.Enter(lockObject, ref lockEntered);
lock body here
}
finally
{
if (lockEntered) Monitor.Exit(lockObject);
}
Enter
è stato scritto con molta attenzione in modo che, indipendentemente dalle eccezioni generate , lockEntered
sia impostato su true se e solo se il blocco è stato effettivamente preso. Se hai requisiti simili, quello che devi fare è effettivamente scrivere:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
e scrivi in modo AllocateResource
intelligente in Monitor.Enter
modo che, indipendentemente da ciò che accade all'interno AllocateResource
, handle
venga compilato se e solo se deve essere deallocato.
Descrivere le tecniche per farlo va oltre lo scopo di questa risposta. Consulta un esperto se hai questo requisito.