2012-05-18 10 views
7

tengo código de prueba como esta:Finalizer no se llama después de excepción no controlada incluso con CriticalFinalizerObject

public class A : CriticalFinalizerObject 
{ 
    ~A() 
    { 
     File.WriteAllText("c:\\1.txt", "1z1z1"); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     A a = new A(); 
     throw new Exception(); 
    } 
} 

principio traté de ejecutarlo sin derivar Una de CriticalFinalizerObject. Finalizer no fue llamado después del final de este programa. Eso me sorprendió ya que pensé que era más determinista, pero está bien. Luego he leído sobre CriticalFinalizerObject's que asegura que se llamarán a sus finalizadores. Derivé A de eso. Adivina qué. Todavía no se ejecuta. ¿Qué estoy haciendo/entendiendo mal?

(Por favor, no escriba cosas obvias sobre el recolector de basura es no determinista, lo sé. No es el caso ya que el programa ha terminado y me imaginaba que podía limpiar con seguridad hasta después de una agradable no controlada lograron excepción .)

Respuesta

9

En primer lugar, vamos a leer sobre CriticalFinalizerObject en MSDN, podemos leer que:

En clases derivadas de la Crítica clase alFinalizerObject, el Common Language Runtime (CLR) garantiza que todos los códigos críticos de finalización tendrán la oportunidad de ejecutarse, siempre que el finalizador siga las reglas para CER, incluso en situaciones donde el CLR forzadamente descarga un dominio de aplicación o cancela un hilo.

La palabra principal aquí es UNLOAD.

En segundo lugar, vamos a leer MSDN de nuevo, esta vez sobre Excepciones en subprocesos administrados:

Si estas excepciones son no controlada en el hilo principal, o en las discusiones que entraron en el tiempo de ejecución de código no administrado, se procede con normalidad, dando como resultado terminación de la aplicación.

La palabra principal es TERMINACIÓN.

Por lo tanto, cuando hay una excepción no controlada en el hilo principal, la aplicación finaliza, pero CriticalFinalizerObject solo ayuda en la descarga del dominio.

Por ejemplo, CriticalFinalizerObject puede ayuda en tal situación:

// Create an Application Domain: 
AppDomain newDomain = AppDomain.CreateDomain("NewApplicationDomain"); 

// Load and execute an assembly: 
newDomain.ExecuteAssembly(@"YouNetApp.exe"); 

//Unload of loaded domain 
AppDomain.Unload(newDomain); 

Esta es una situación en la que se descargó de dominio, y CriticalFinalizerObject garantizo que su finalizador se llamará.

En su situación con la terminación de la aplicación se puede tratar de suscribirse a

AppDomain.CurrentDomain.UnhandledException 

y finalizar manualmente los objetos.

UPD: Jeffrey Richter en su libro "CLR a través de C#" escribió sobre CriticalFinalizerObject, que es para situaciones en las que envíe el código de ejemplo para SQL Server, que puede funcionar como un C# procedimientos. En tal caso, CriticalFinalizerObject le ayuda a limpiar su objeto, si SQLServer descargará el dominio de su biblioteca. También CriticalFinalizerObject es para situaciones donde necesita en el finalizador del método object to call de otro objeto, porque CriticalFinalizerObject le garantiza que se llamará a su finalizador después de los finalizadores de todos los objetos no CriticalFinalizerObject.

+0

¡Gracias por la respuesta detallada! Diría que palabras como "terminado" vs "descargado" no están muy bien definidas en estos documentos MS. En sentido común, tener algo "terminado" no significa necesariamente que no será "descargado", aunque como vemos no es así aquí ... – IlyaP

+0

Agregué algunas actualizaciones para responder de "CLR a través de C#" – igofed

0

Ok. Escribió una prueba simple. Si tengo una excepción en el constructor donde creo mi objeto A, de hecho no pude hacer que el finalizador funcionara, pero cuando creé un objeto A no en el constructor de otra clase sino en otro método, y luego lancé una excepción, funcionó.

Así que sospecho que si el constructor nunca terminó de construir la clase, y su creación finalizó, el objeto nunca se crea, la pila se borra, los objetos se eliminan del montón sin finalización como nunca.

Es mi suposición. Pero para resolver el problema, me gustaría ajustar el código de construcción crítico construyendo objetos en try-catch-finally y llamar al código de limpieza explícitamente.

Ejemplo: Esto funcionó

public Form1() 
     { 
      InitializeComponent(); 
     } 
     protected override void OnLoad(EventArgs e) 
     { 
      base.OnLoad(e); 
      var a = new A(); 
      throw new Exception(); 
     } 
    } 

esto no

public Form1() 
     { 
      InitializeComponent(); 
      var a = new A(); 
      throw new Exception(); 
     } 

    } 
+0

No se llama, como ve, creo un archivo en el finalizador, nunca aparece. – IlyaP

+0

GC.Collect() ¿dónde? Si antes de lanzar la excepción, entonces seguramente funcionará. Pero no puedo escribir ningún código después de lanzar ... – IlyaP

+1

Para manipular sin manejar aún puede usar el evento 'AppDomain.CurDomain.UnhandledException'. –

Cuestiones relacionadas