7

我有这样的测试代码:

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();
    }
}

首先,我尝试在不从 CriticalFinalizerObject 派生 A 的情况下运行它。此程序结束后未调用终结器。这让我感到惊讶,因为我认为它更具确定性但还可以。然后我阅读了关于确保调用其终结器的 CriticalFinalizerObject。我从中得出 A。你猜怎么着。它仍然没有被执行。我在做什么/理解错误?

(请不要写关于垃圾收集器是非确定性的明显内容,我知道。程序结束时并非如此,我想我可以在一个很好的未处理的托管异常之后安全地清理。)

4

2 回答 2

10

首先,让我们读一下MSDN中的CriticalFinalizerObject,我们可以读到:

在从 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);

在这种情况下,域被卸载,CriticalFinalizerObject 向您保证,您的终结器将被调用。

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

AppDomain.CurrentDomain.UnhandledException

并手动完成您的对象。

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

于 2012-05-18T17:00:53.003 回答
0

好的。写了一个简单的测试。如果我在创建 A 对象的构造函数中有异常,确实无法使终结器工作,但是当我创建 A 对象时,不是在其他类的构造函数中而是在其他方法中,然后抛出异常它起作用了。

所以我怀疑,如果构造函数从未完成构造类,并且它的创建终止,则永远不会创建对象,清除堆栈,从堆中删除对象而没有完成,就像它们从未发生过一样。

这是我的猜测。但是为了解决这个问题,我会在 try-catch-finally 中包装构建对象的关键构造代码,并显式调用清理代码。

示例:这行得通

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

这并没有

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

   }
于 2012-05-18T16:14:07.033 回答