2012-05-18 7 views
7

Ho codice test come questo:Finalizer non chiamato dopo che l'eccezione non gestita anche 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(); 
    } 
} 

Per prima cosa ho provato a fare funzionare senza derivante A da CriticalFinalizerObject. Finalizer non è stato chiamato dopo la fine di questo programma. Questo mi ha sorpreso perché pensavo fosse più deterministico ma okay. Poi ho letto su CriticalFinalizerObject che assicura che i loro finalizzatori vengano chiamati. Ne ho tratto A. Indovina cosa. Ancora non viene eseguito. Cosa sto facendo/capendo male?

(Si prega di non scrivere cose ovvie su garbage collector essere non-deterministico, lo so. Non è il caso in quanto il programma è finito e ho immaginato che potrei tranquillamente pulire dopo un bel gestita gestito eccezione .)

risposta

9

In primo luogo, leggiamo su CriticalFinalizerObject in MSDN, si legge, che:

In classi derivate dalla Critica classe alFinalizerObject, Common Language Runtime (CLR) garantisce che a tutti i codici di finalizzazione critici verrà data l'opportunità di eseguire, a condizione che il finalizzatore segua le regole per un CER, anche in situazioni in cui il CLR forzatamente scarica un dominio applicazione o interrompe un filo.

La parola principale qui è UNLOAD.

In secondo luogo, leggiamo MSDN di nuovo, questa volta sulle eccezioni in thread gestiti:

Se queste eccezioni sono non gestita nel thread principale, o in fili che sono entrate nel runtime da codice non gestito, che procede normalmente, con conseguente terminazione dell'applicazione.

La parola principale è TERMINAZIONE.

Così, quando c'è un'eccezione non gestita nel thread principale - l'app termina, ma CriticalFinalizerObject aiuta solo a scaricare il Dominio.

Per esempio, CriticalFinalizerObject può aiuta in tale situazione:

// 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); 

questa è una situazione, in cui è stato scaricato il dominio e CriticalFinalizerObject garantirvi, che il finalizzatore verrà chiamato.

Nella tua situazione con terminazione di applicazione che si può provare a iscriversi a

AppDomain.CurrentDomain.UnhandledException 

e finalizzare manualmente gli oggetti.

UPD: Jeffrey Richter nel suo libro "CLR via C#", ha scritto su CriticalFinalizerObject, che è per le situazioni in cui si invia il codice, ad esempio a SQLServer, che può essere eseguito C# come procedure. In tal caso, CriticalFinalizerObject ti aiuta a pulire il tuo oggetto, se SQLServer scaricherà il dominio della tua libreria. Anche CriticalFinalizerObject è per le situazioni in cui è necessario nel finalizzatore dell'oggetto chiamare il metodo di un altro oggetto, poiché CriticalFinalizerObject garantisce che il suo finalizzatore verrà chiamato dopo i finalizzatori di tutti gli oggetti non CriticalFinalizerObject.

+0

Grazie per la risposta dettagliata! Direi che parole come "terminato" o "scaricato" non sono molto ben definite in questi documenti MS. Nel senso comune, avere qualcosa "terminato" non significa necessariamente che non sarà "scaricato", anche se come vediamo qui non è così ... – IlyaP

+0

Ho aggiunto alcuni aggiornamenti per rispondere da "CLR via C#" – igofed

0

Ok. Ha scritto un semplice test. Se ho un'eccezione nel costruttore dove creo il mio oggetto A, in effetti non potevo far funzionare il finalizzatore, ma quando ho creato un oggetto non nel costruttore di un'altra classe ma in un altro metodo, e poi lanciavo un'eccezione funzionava.

Quindi ho il sospetto che se il costruttore non ha mai terminato la costruzione della classe e la sua creazione è terminata, l'oggetto non viene mai creato, lo stack cancellato, gli oggetti vengono rimossi dall'heap senza finalizzazione come mai accadono.

È una mia ipotesi. Ma per risolvere il problema, mi piacerebbe racchiudere in modo critico la costruzione del codice nella costruzione di oggetti in try-catch-finally e chiamare esplicitamente il codice clean up.

Esempio: questo ha lavorato

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

Questo non ha fatto

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

    } 
+0

Non viene chiamato - come si vede, creo un file nel finalizzatore - non appare mai. – IlyaP

+0

GC.Collect() dove? Se prima di lanciare un'eccezione, sicuramente funzionerà. Ma non posso scrivere alcun codice dopo il lancio ... – IlyaP

+1

Per gestire unhandled puoi comunque utilizzare l'evento 'AppDomain.CurDomain.UnhandledException'. –

Problemi correlati