我知道c#中的析构函数没有执行顺序。
以下结构我确实在几个类中使用,它用于Desturct实例和静态信息:
public class MyClass
{
private static readonly Destructor DestructorObject = new Destructor();
~MyClass()
{
Console.WriteLine("Destructor Called");
}
static void Main(string[] args)
{
var myClass = new MyClass();
}
private sealed class Destructor
{
~Destructor()
{
Console.WriteLine("Static Destructor Called");
}
}
}
正如我上面提到的,析构器的顺序是不确定的。但是,当我在许多类中使用这种构造时,我发现在每个类中都有一个不变的顺序,即使我重新编译应用程序并再次运行它,这个顺序也会保持不变。
意味着一个MyClass1
总是可以首先运行~MyClass1
,而另一个类MyClass2
总是可以先运行~Destructor
。
由于每个类都有一个"隐藏"的顺序,我可以信任它吗
正如明显存在的那样;隐藏的";每节课的订单,我可以相信吗?
不,你不能。如果你看一下文件,他们会尖叫:
两个对象的终结器不能保证在任何特定顺序,即使一个对象引用另一个对象。也就是说,如果对象A具有对对象B的引用,并且两者都具有终结器,对象当对象A的终结器时,B可能已经被终结启动。
将这样的实现细节作为正常执行流的一部分是一个非常糟糕的主意。
鉴于出于某种原因,您选择使用终结器来清理静态资源,您应该首先考虑这是否是正确的方法,考虑到析构函数所暗示的一切,然后至少实现IDisposable
,并给调用者一个处理资源的机会,同时调用GC.SupressFinalize
。
在对象中使用这种常见方法也会延长对象的寿命,因为只有当它们移动到f-reachable队列时,它们才有资格被收集,并且依赖终结器线程来实际清理它们,这根本不能保证。
Eric Lippert最近(2015年5月18日(开始了一个名为的系列节目。当你所知道的一切都是错误的谈论终结器神话时,我建议你看看。
编辑:
有趣的是,Erics系列的第二篇帖子(今天发布(回答了这个问题:
神话:终结器以可预测的顺序运行
假设我们有一个对象树,所有对象都是可终结的,并且都在终结器队列上。树从根到叶、从叶到根或任何其他顺序都不需要最终确定。
正如前面多次指出的那样,终结器不保证任何顺序。你不能做任何假设。你应该假设最坏的情况(即(它可以按任何顺序执行。
由于每个类都有一个"隐藏"的顺序,我能相信吗?
是和否
对于普通对象,终结器的执行顺序是不可预测的。所以没有。你不能依赖它。
对于从SafeHandle
继承的对象,是的,有一些排序。例如:如果有两个对象准备好进行终结,一个是从SafeHandle
派生的,另一个不是,那么可以保证在执行SafeHandle的终结器之前,将执行不是从SafeHandle
继承的对象的终结器。
这种排序存在的原因在本博客文章中有描述。这并没有记录在案,而是由BCL团队在博客上发表的。未来不太可能改变。但是
为了证明这一点,下面的程序总是先完成Destructor
类,然后再完成MyClass
。因为您知道MyClass
继承自SafeHandle
。
public class MyClass : SafeHandle
{
private static readonly Destructor DestructorObject = new Destructor();
~MyClass()
{
Console.WriteLine("Destructor Called");
}
protected override bool ReleaseHandle()
{
return true;
}
public override bool IsInvalid
{
get { return false; }
}
static void Main(string[] args)
{
var myClass = new MyClass(IntPtr.Zero, true);
}
private sealed class Destructor
{
~Destructor()
{
Console.WriteLine("Static Destructor Called");
}
}
public MyClass(IntPtr invalidHandleValue, bool ownsHandle)
: base(invalidHandleValue, ownsHandle)
{
}
}
我建议阅读Eric Lippert的最新博客文章,但由于订单没有定义,您不能依赖订单在未来版本中保持不变。