ACTUALIZACIÓN : utilicé esta pregunta como base para un artículo que se puede encontrar aquí ; consúltelo para una discusión adicional sobre este tema. ¡Gracias por la buena pregunta!
Aunque la respuesta de Schabse es, por supuesto, correcta y responde a la pregunta que se hizo, hay una variante importante en su pregunta que no hizo:
¿Qué sucede si se font4 = new Font()lanza después de que el constructor asignó el recurso no administrado pero antes de que el ctor regrese y complete font4con la referencia?
Déjame aclararlo un poco más. Supongamos que tenemos:
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;
}
}
}
Ahora tenemos
using(Foo foo = new Foo())
Whatever(foo);
Esto es lo mismo que
{
Foo foo = new Foo();
try
{
Whatever(foo);
}
finally
{
IDisposable d = foo as IDisposable;
if (d != null)
d.Dispose();
}
}
OKAY. Supongamos Whateverlanzamientos. Luego, el finallybloque se ejecuta y el recurso se desasigna. No hay problema.
Supongamos Blah1()lanzamientos. Luego, el lanzamiento ocurre antes de que se asigne el recurso. El objeto ha sido asignado pero el ctor nunca regresa, por foolo que nunca se completa. tryNunca ingresamos, por lo que nunca ingresamos finallytampoco. La referencia del objeto se ha quedado huérfana. Finalmente, el GC lo descubrirá y lo pondrá en la cola del finalizador. handlesigue siendo cero, por lo que el finalizador no hace nada. Observe que se requiere que el finalizador sea robusto frente a un objeto que se está finalizando cuyo constructor nunca se completó . Usted está obligado a escribir finalizadores que son tan fuerte. Esta es otra razón más por la que debería dejar los finalizadores de escritura a expertos y no intentar hacerlo usted mismo.
Supongamos Blah3()lanzamientos. El lanzamiento ocurre después de que se asigna el recurso. Pero nuevamente, foonunca se completa, nunca ingresamos finallyy el objeto es limpiado por el hilo del finalizador. Esta vez, el identificador no es cero y el finalizador lo limpia. De nuevo, el finalizador se ejecuta en un objeto cuyo constructor nunca tuvo éxito, pero el finalizador se ejecuta de todos modos. Obviamente debe porque esta vez, tenía trabajo que hacer.
Ahora suponga que Blah2()lanza. ¡El lanzamiento ocurre después de que se asigna el recurso pero antes de que handle se complete! Nuevamente, el finalizador se ejecutará, pero ahora handlesigue siendo cero y ¡filtramos el controlador!
Debe escribir un código extremadamente inteligente para evitar que ocurra esta fuga. Ahora, en el caso de su Fontrecurso, ¿a quién diablos le importa? Filtramos un identificador de fuente, gran cosa. Pero si necesita de manera absolutamente positiva que todos los recursos no administrados se limpien sin importar el momento de las excepciones, entonces tiene un problema muy difícil en sus manos.
El CLR tiene que resolver este problema con bloqueos. Desde C # 4, los bloqueos que usan la lockdeclaración se han implementado así:
bool lockEntered = false;
object lockObject = whatever;
try
{
Monitor.Enter(lockObject, ref lockEntered);
lock body here
}
finally
{
if (lockEntered) Monitor.Exit(lockObject);
}
Enterse ha escrito con mucho cuidado para que, independientemente de las excepciones que se generen , lockEnteredse establezca en verdadero si y solo si el bloqueo se tomó realmente. Si tiene requisitos similares, entonces lo que necesita es escribir:
public Foo()
{
Blah1();
AllocateResource(ref handle);
Blah2();
Blah3();
}
y escriba AllocateResourceinteligentemente como Monitor.Enterpara que, pase lo que pase en el interior AllocateResource, handlese rellene si y sólo si es necesario desasignarlo.
Describir las técnicas para hacerlo está más allá del alcance de esta respuesta. Consulte a un experto si tiene este requisito.