Los finalizadores son necesarios para garantizar la liberación de recursos escasos de vuelta al sistema, como manejadores de archivos, sockets, kernel, etc. Dado que el finalizador siempre se ejecuta al final del objetos de la vida, es el lugar designado para liberar esos mangos.
El patrón Dispose
se utiliza para proporcionar la destrucción determinística de los recursos. Como el recolector de elementos no utilizados en tiempo de ejecución de .net no es determinista (lo que significa que nunca puede estar seguro de cuándo el tiempo de ejecución recogerá los objetos antiguos y llamará a su finalizador), se necesitaba un método para garantizar la liberación determinista de los recursos del sistema. Por lo tanto, cuando implementa correctamente el patrón Dispose
, proporciona una liberación determinística de los recursos y, en los casos en que el consumidor es descuidado y no elimina el objeto, el finalizador lo limpiará.
Un simple ejemplo de por qué Dispose
se necesita podría ser un método de registro rápido y sucio:
public void Log(string line)
{
var sw = new StreamWriter(File.Open(
"LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None));
sw.WriteLine(line);
// Since we don't close the stream the FileStream finalizer will do that for
// us but we don't know when that will be and until then the file is locked.
}
En el ejemplo anterior, el archivo permanecerá bloqueado hasta que el recolector de basura llama el finalizador en el objeto StreamWriter
. Esto presenta un problema ya que, mientras tanto, el método podría llamarse nuevamente para escribir un registro, pero esta vez fallará porque el archivo todavía está bloqueado.
La forma correcta es disponer el objeto cuando ha dejado de utilizarla:
public void Log(string line)
{
using (var sw = new StreamWriter(File.Open(
"LogFile.log", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None))) {
sw.WriteLine(line);
}
// Since we use the using block (which conveniently calls Dispose() for us)
// the file well be closed at this point.
}
Por cierto, técnicamente finalizadores y destructores significan lo mismo; Prefiero llamar finalizadores de C# 'finalizadores' ya que de lo contrario tienden a confundir a las personas con destructores de C++, que a diferencia de C#, son deterministas.
IMO esta es la mejor respuesta aquí. La parte más importante de esto, y por qué utilizamos la sintaxis desechable, es proporcionar la * liberación determinista * de recursos escasos. Buena publicación. – Rob
Buena respuesta, aunque los finalizadores no se ejecutan automáticamente al final de la vida de los objetos. De lo contrario, no necesitaríamos el patrón desechable. GC los llama cuando determina que necesita ejecutarlos (quién sabe cuándo). –
Solo para el registro. No se garantiza la ejecución de los finalizadores. Se ejecutan secuencialmente por un hilo dedicado, por lo que si un finalizador ingresa en un punto muerto, no se ejecutarán otros finalizadores (y se perderá memoria). Obviamente, el finalizador no debería bloquear, pero solo estoy diciendo que hay advertencias. –