C#析构函数在哪里?

3
我有一个类,它生成另一个UI线程并执行其任务。当父类被销毁时,我需要终止该线程并清理它。那么如何知道我的父类何时被销毁?
作为C++程序员,我的第一个想法是将此代码放在析构函数中。但是C#实际上没有析构函数 - 只有finalizers和dispose - 从我所了解的情况来看,这些可能会或可能不会被调用(我猜这取决于GC的心情?)。
如果您可能或可能不想释放资源,那就很好和简单。
但是,您应该将必须被执行的代码放在哪里,无论何时对象被销毁?

1
我认为C#没有这个概念。如果你想要确定性的清理,大多数人会使用IDisposable/using来处理它。但是没有办法强制调用.Dispose(),就像在C++中没有办法强制客户端销毁一个对象一样。 - recursive
1
在C#中,对象在超出范围时不会被销毁。因此,如果问题是“每当对象超出范围时是否自动执行”,答案是“不可能”。否则,使用析构函数。 - Jon
请注意,dispose是确定性的。根据GC的心情,可能不会调用终结器,但处理不是一个选项,也不能延迟。 - recursive
2
你真的不应该使用多个UI线程。除非你确信没有其他选择,否则最终只会让自己陷入无尽的痛苦之中。 - Servy
这个相关问题上有一个很有帮助的答案:https://dev59.com/iXRB5IYBdhLWcg3w9Lzn - Ryan Kohn
4个回答

5

您需要将其放入Dispose(实现IDisposable接口),然后确保在不再需要对象时调用Dispose。有一种语言结构可以做到这一点:

using (var foo = new Foo())
{
    // Do something with foo.
}

foo.Dispose会在using块的末尾被调用。这相当于:

{
    var foo = new Foo();
    try
    {
        // Do something with foo.
    }
    finally
    {
        foo.Dispose();
    }
}

请注意,当对象离开作用域时,不会自动调用Dispose;您需要使用using块或显式调用它自己来处理。但是,您应该在Foo中提供一个finalizer来调用Dispose,以便如果对象在GC到达之前没有被处理,您不会留下未释放的资源:
~Foo()
{
    Dispose();
}

IDisposable模式背后的思想是,它可以明确地告诉你一个类何时需要释放资源。 这里有一篇文章介绍如何正确实现它(考虑到可能存在子类):文章链接


是的。C#最接近RAII的方式。它要求您在“using”块中包装使用资源的代码,但是......这就是它的实现方式。 - jalf
这似乎是我唯一可用的东西。但这似乎是语言上的缺陷。因为它依赖于使用此类的人知道他必须使用using{}块。如果他不这样做,就会发生糟糕的事情。我一直认为类应该在不依赖开发人员了解有关如何针对给定类进行清理的任何细节的情况下自行清理。GC非常适合释放字符串等 - 但似乎我已经找到了它的致命弱点... - William Madonna Jr.
@WilliamMadonnaJr。如果一个类实现了IDisposable,那么你应该总是在使用完后调用Dispose。这个接口和相关的模式告诉你这个类持有需要释放的非托管资源。这里有一篇关于如何正确实现这个模式的文章:http://www.codeproject.com/Articles/15360/Implementing-IDisposable-and-the-Dispose-Pattern-P - Will Vousden

3

当一个对象被销毁时,没有办法保证一定会执行某些操作。例如,试图拔掉电脑的插头,finalizer 就不会被调用。

最好的办法是使用在 C# 中使用 C++ 析构函数语法定义的 finalizer。不过,更好的做法是实现 IDisposable 接口,并使用 using{} 块。


1
真的,但并不是很有帮助。 :) 我认为你可以安全地假设“绝对肯定会被执行”意味着“除非你拔掉电脑的插头” ;) - jalf
哈哈!你说得对,詹姆斯 - 但是jalf已经明白了这个想法。显然,如果有人拔掉插头,我不太担心中止我的次要线程 - 物理学会为我解决这个问题。 - William Madonna Jr.
我的观点是,防止对象销毁的不仅仅是这一点,因此如果你绝对必须...那么你必须找到一些外部方法来保证。 如果它仅仅是“必须被调用 - 除非特殊情况”,那么一旦你列举出允许和不允许的特殊情况,你可能会发现终结器是可以的。 - James Curran

1
尽管不常见,您可以使用C#中的~字符来定义“析构函数”。示例如下:
class Parent
{
    ~Parent()  // destructor
    {
        // cleanup statements...
    }
}

注意:
析构函数会隐式调用对象的基类上的Finalize方法。
来源

2
你可以这样做,但它并不会像C++中的析构函数一样被创建,因此并没有什么帮助。 - jalf

0
你可以将你的类定义为一个 Component,并将包含它的类定义为一个 Container
参见:C# - 什么是组件,通常如何使用? 其他答案似乎都忙于处理“超出范围”的用例。但是,“每当我的父类被销毁”?如果“父类”是一个“包含对象”,那么组件就是正确的工具。

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