我知道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");
}
}
}
正如我上面提到的,Destuctors的顺序是不确定的。但是当我在许多类中使用这个构造时,我发现,在每个类中都有一个不变的顺序,即使我重新编译应用程序并再次运行它也会保持不变。
意味着MyClass1
可以首先运行~MyClass1
而另一个MyClass2
可以首先运行~Destructor
。
由于每个班级显然都有“隐藏”的订单,我能相信吗?
答案 0 :(得分:7)
因为显然有一个"隐藏"每个班级的订单,我能相信吗?
不,你不能。如果你看the docs,他们会尖叫:
两个对象的终结器无法保证在任何对象中运行 特定顺序,即使一个对象引用另一个对象。也就是说,如果 对象A具有对象B的引用,并且都具有终结符Object 在对象A的终结器时,B可能已经完成 启动。
依赖这样的实现细节作为正常执行流程的一部分将是一个非常糟糕的主意。
由于某些原因,您选择使用终结器作为清理静态资源的方法,您应首先考虑这是否是正确的方法,taking into account everything destructors imply,然后至少实现IDisposable
并为调用者提供处理资源的机会,同时调用GC.SupressFinalize
。
将此作为对象中的常用方法也会导致对象延长其使用寿命,因为只有在移动到f-reachable队列后才有资格进行收集,并依赖终结器线程实际清理它们起来,根本没有保证。
Eric Lippert最近(18-05-2015)创办了一个名为When everything you know is wrong的系列,谈论终结者的神话,我建议你看看。
修改强>
有趣的是,该系列中的Erics second post(今天发布)回答了这个问题:
神话:终结者以可预测的顺序运行
假设我们有一个对象树,所有对象都可以终结,并且都在终结器队列中。从树根到树叶,从树叶到根,或任何其他顺序,都没有要求树最终确定。
答案 1 :(得分:2)
如前几次尖叫,终结者不保证任何订单。你无法做出任何假设。你应该假设最坏的(即)它可以按任何顺序执行。
因为显然有一个"隐藏"每个班级的订单,我能相信吗?
是和否。
对于普通对象,终结器的执行顺序是不可预测的。所以不行。你不能依赖它。
对于继承自SafeHandle
的对象,是的,有一些排序。例如:如果你有两个对象准备完成,一个是从SafeHandle
派生而另一个不是,那么保证不会从Destructor
继承的对象的终结器将是在执行SafeHandle的终结器之前执行。
这种排序的存在是出于this blog post中描述的原因。这个没有记录,但是由BCL团队发布。将来不太可能改变。但...
为了证明这一点,以下程序将始终首先完成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)
{
}
}
。
display: block
答案 2 :(得分:1)
我建议阅读Eric Lippert's latest blog post,但由于订单未定义,您不能依赖未来版本中保持相同的订单。