静态析构函数

85

C#有静态构造函数,用于进行一些初始化操作(比如进行一些非托管资源的初始化)。

我想知道是否有静态析构函数?


1
+1 给这个有趣的评论,leppie。我想指出静态资源的生命周期大多与应用程序相同。它们会在应用程序终止时消失。因此,不需要静态析构函数。 - decyclone
5
“所以不需要静态析构函数”和“这两件事情有什么关系?”--它们之间有什么联系?因为只有在应用程序域被卸载时才会发生,所以突然就不必要了吗?我不确定我是否理解了其中的逻辑。 - BrainSlugs83
6个回答

117

虽然不是真正的析构函数,但以下是实现方式:

class StaticClass 
{
   static StaticClass() {
       AppDomain.CurrentDomain.ProcessExit +=
           StaticClass_Dtor;
   }

   static void StaticClass_Dtor(object sender, EventArgs e) {
        // clean it up
   }
}

3
尽管该事件似乎与 AppDomain 有关,但实际上只有在默认域退出时(即最后一个域),整个应用程序关闭之前才会触发。如果任何其他的 AppDomain 被卸载,它将不会被触发,这本身会导致大多数类型和类型实例被销毁(但不总是)。此事件不能保证始终触发。参考链接:http://blogs.msdn.com/b/jmstall/archive/2006/11/26/process-exit-event.aspx。 - Abel
1
我建议不要将敏感的清理操作留给静态实例的析构函数,按照这个建议,您永远不需要担心此事件不会触发。我从未遇到过此事件未触发的问题(即使应用程序中出现意外异常)。通过任务管理器终止进程与出现意外异常有很大的不同。尽管如此,如果用户通过操作系统任务管理系统终止程序,则它将不会触发。 - zackery.fix
1
将销毁代码添加到创建应用程序域的Main()方法末尾是否是一个好主意? - florien

79

这是最好的方法(参考:https://dev59.com/xnVC5IYBdhLWcg3wjx1d#256278

public static class Foo
{
    private static readonly Destructor Finalise = new Destructor();

    static Foo()
    {
        // One time only constructor.
    }

    private sealed class Destructor
    {
        ~Destructor()
        {
            // One time only destructor.
        }
    }
}

5
这种方法保证能够奏效!:) 不妨一试,我有单元测试可以证明它的有效性,如果有人感兴趣的话? - Tod Thomson
1
这是一种与环境无关的方式(在语言逻辑内部构建)。 - eMeL
10
很遗憾,这并*不保证有效。根据雷蒙德·陈(Raymond Chen)关于垃圾回收的文章,终结器不可靠,因为不能保证垃圾回收器会运行。 - McGuireV10
2
@TodThomson 对于.NET Framework,~Object Remarks 部分列出了一些不保证会调用终结器的情况,而您提供的文档指出在.NET Core下它们不会在关闭时被调用。不幸的是,微软文档的质量已经不如以前了。 - McGuireV10
你能解释一下如何使用那种方法吗?我需要在某个地方调用 Finalise 吗,还是当 Foo 被销毁时它会自动被调用? - anatol
显示剩余2条评论

41

不,没有。

静态析构函数据说会在进程执行结束时运行。当一个进程终止时,所有与其关联的内存/句柄都将被操作系统释放。

如果你的程序在执行结束时应该执行特定操作(例如事务性数据库引擎刷新其缓存),那么正确处理它比只运行在进程正常执行结束时的代码要困难得多。您必须手动处理进程崩溃和意外终止,并尝试在下次运行时恢复。 "静态析构函数" 的概念并不能帮助解决这个问题。


6
在我的情况下,我在使用一个全局互斥体(Mutex),需要释放它,否则在下一次运行应用程序时会抛出 AbandonedMutexException 异常。在这种情况下,程序结束时释放的内存和句柄并没有考虑到我的 Mutex。你在第三段中提到的方法可能是解决方法之一,但对我来说,在应用程序结束时释放更有意义。 - user1132959
在我的情况下,这是一个全局消息传递(非托管)线程,在结束时必须正确关闭,否则它会崩溃。zackery.fix的答案对我非常有帮助。 - ulatekh

18

没有,你能做的最接近的方法是在 AppDomain 上设置一个时间处理程序,将其设置为 DomainUnload 事件,并在那里执行清理。


4
这实际上是一种更受欢迎的方法,与得到更高票数的"Process_Exit"建议相比有很大不同。在静态构造函数触发后,类型的生命周期通常是程序集的生命周期,而随着应用程序域的卸载而卸载。 - Abel
1
这对我来说是最简单且影响最小的解决方案。在我的情况下,钩入Process_Exit无效,但DomainUnload可以。 - Brian Wirt
但是DomainUnload对于默认域不会被调用吗? - ulatekh

7

在静态实现中初始化和清理非托管资源是相当棘手的,容易出现问题。

为什么不使用单例,并为该实例实现一个Finalizer(最好继承自SafeHandle)呢?


1
没有像静态类一样的析构函数,但是如果你真的需要做一些事情,你可以使用Appdomain.Unloaded事件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接