即使使用CriticalFinalizerObject,在未处理的异常之后也不会调用终结器

Ily*_*yaP 7 .net c# finalizer

我有这样的测试代码:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

首先,我尝试在不从CriticalFinalizerObject派生A的情况下运行它.在该程序结束后没有调用终结器.这令我感到惊讶,因为我认为这更具确定性,但没关系.然后我读到了关于CriticalFinalizerObject的内容,确保调用它们的终结器.我从它那里得到了A. 你猜怎么着.它仍然没有被执行.我在做什么/理解错误?

(请不要写关于垃圾收集器是非确定性的明显的东西,我知道.事实并非如此,因为程序结束了,我想我可以安全地清理一个很好的未处理的托管异常.)

igo*_*fed 9

首先,让我们了解CriticalFinalizerObjectMSDN中,我们可以读出,即:

在从CriticalFinalizerObject类派生的类中,公共语言运行库(CLR)保证所有关键的最终化代码都有机会执行,前提是终结器遵循CER的规则,即使在CLR强制卸载应用程序域的情况下也是如此或中止一个帖子.

这里的主要词是UNLOAD.

其次,让我们再次阅读MSDN,这次是关于托管线程中的异常:

如果在主线程或从非托管代码进入运行时的线程中未处理这些异常,则它们会正常进行,从而导致应用程序终止.

主要词是TERMINATION.

因此,当主线程中存在未处理的异常时 - 应用程序终止,但CriticalFinalizerObject仅帮助卸载域.

例如,CriticalFinalizerObject可以帮助处理这种情况:

// 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);
Run Code Online (Sandbox Code Playgroud)

这是一种情况,其中域已被卸载,而CriticalFinalizerObject保证您将调用终结器.

在终止应用程序的情况下,您可以尝试订阅

AppDomain.CurrentDomain.UnhandledException
Run Code Online (Sandbox Code Playgroud)

并手动完成对象.

UPD: Jeffrey Richter在他的书"CLR via C#"中写了一篇关于CriticalFinalizerObject的文章,它是针对您将代码发送到SQLServer的情况,SQLServer可以将C#作为程序运行.在这种情况下,如果SQLServer将卸载您的库的域,CriticalFinalizerObject可以帮助您清理对象.此外,CriticalFinalizerObject适用于您需要在对象的终结器中调用另一个对象的方法的情况,因为CriticalFinalizerObject保证您将在所有非CriticalFinalizerObject对象的终结器之后调用它的终结器.