Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/267.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 有人能解释这种最终定稿行为吗_C#_.net 4.0_Clr_Destructor_Finalizer - Fatal编程技术网

C# 有人能解释这种最终定稿行为吗

C# 有人能解释这种最终定稿行为吗,c#,.net-4.0,clr,destructor,finalizer,C#,.net 4.0,Clr,Destructor,Finalizer,在“调查”最终定稿(阅读:尝试愚蠢的事情)时,我偶然发现了一些意想不到的行为(至少对我而言) 我希望finalize方法不会被调用,而它会被调用两次 class Program { static void Main(string[] args) { // The MyClass type has a Finalize method defined for it // Creating a MyClass places a reference to

在“调查”最终定稿(阅读:尝试愚蠢的事情)时,我偶然发现了一些意想不到的行为(至少对我而言)

我希望finalize方法不会被调用,而它会被调用两次

class Program
{
    static void Main(string[] args)
    {
        // The MyClass type has a Finalize method defined for it
        // Creating a MyClass places a reference to obj on the finalization table.
        var myClass = new MyClass();

        // Append another 2 references for myClass onto the finalization table.
        System.GC.ReRegisterForFinalize(myClass);
        System.GC.ReRegisterForFinalize(myClass);
        // There are now 3 references to myClass on the finalization table.

        System.GC.SuppressFinalize(myClass);
        System.GC.SuppressFinalize(myClass);
        System.GC.SuppressFinalize(myClass);

        // Remove the reference to the object.
        myClass = null;

        // Force the GC to collect the object.
        System.GC.Collect(2, System.GCCollectionMode.Forced);

        // The first call to obj's Finalize method will be discarded but
        // two calls to Finalize are still performed.
        System.Console.ReadLine();
    }
}

class MyClass
{
    ~MyClass()
    {
        System.Console.WriteLine("Finalise() called");
    }
}
有人能解释一下这种行为是否是故意的吗?如果是,为什么

上述代码是在x86调试模式下编译的,并在CLR v4上运行


非常感谢

有趣的数据点:

  • linux上的mono 2.10.8.1不调用终结器
  • linux上的mono 2.8不调用终结器:
  • Win32上的mono 2.8.1不调用终结器
  • Win32上的mono 2.6.7不调用终结器

  • Win32上的.NET 3.5调用终结器两次

测试代码供参考:

class Program
{
    static void Main(string[] args)
    {
        // The MyClass type has a Finalize method defined for it
        // Creating a MyClass places a reference to obj on the finalization table.
        var myClass = new MyClass();

        // Append another 2 references for myClass onto the finalization table.
        System.GC.ReRegisterForFinalize(myClass);
        System.GC.ReRegisterForFinalize(myClass);
        // There are now 3 references to myClass on the finalization table.

        System.GC.SuppressFinalize(myClass);
        System.GC.SuppressFinalize(myClass);
        System.GC.SuppressFinalize(myClass);

        // Remove the reference to the object.
        myClass = null;

        // Force the GC to collect the object.
        System.GC.Collect(2, System.GCCollectionMode.Forced);

        // The first call to obj's Finalize method will be discarded but
        // two calls to Finalize are still performed.
    }
}

class MyClass
{
    ~MyClass()
    {
        System.Console.WriteLine("Finalise() called");
    }
}

我不知道是什么导致了这种奇怪的行为。但是,由于您违反了该方法的使用记录,任何事情都可能发生。ReRegisterForFinalize的文档说明:

请求系统为以前调用过SuppressFinalize的指定对象调用终结器。

在调用ReRegisterForFinalize之前,您没有调用SuppressFinalize。文档没有说明在这种情况下会发生什么,事实上,很明显,发生了一些非常奇怪的事情

不幸的是,同一文档页接着显示了一个示例,其中对尚未调用SuppressFinalize的对象调用了ReRegisterForFinalize

这有点乱。我会和文档经理商量的


当然,这个故事的寓意是,如果你违反了文档中描述的规则,那么就停止违反这些规则。我怀疑这属于“未定义行为”的范畴。如果您查看和的文档,他们会说:

obj参数必须是此方法的调用方

你的代码不是这样的。

我猜。。。这真的只是一个猜测。正如埃里克所说,不要打破这样的规则:)这种猜测只是为了无聊的投机和兴趣

我怀疑涉及两种数据结构:

  • 终结队列
  • 对象的标题
当GC注意到某个对象符合垃圾收集条件时,我怀疑它会检查该对象的头并将引用添加到终结队列。您对
SuppressFinalization
的调用阻止了这种行为

另外,终结器线程通过终结队列运行,并为找到的所有内容调用终结器。您对
ReRegisterForFinalize
的调用绕过了引用在队列中结束的正常方式,并直接添加了引用
SuppressFinalization
并没有从队列中删除引用-它只是阻止引用以正常方式添加到队列中


所有这些都可以解释你所看到的行为(我也复制了这些行为)。它还解释了为什么当我删除
SuppressFinalization
调用时,我最终会看到终结器被调用了三次——因为在这种情况下,“正常”路径也会将引用添加到终结队列中。

如果将其中一个
SuppressFinalization
调用移到两个
ReRegisterForFinalization
之前,它只被调用一次。基本上,在
ReRegisterForFinalize
调用之间必须至少有1个
SuppressFinalize
交错调用。谢谢@Eric,我知道代码违反了规范,我肯定不会提倡在合理的程序中使用上述任何一个。嘿,受虐狂也是程序员。不要践踏少数群体,兄弟。很好的拾取,尽管有趣的是,如果在
重新注册forfinalize
调用之间不交错至少1次
SuppressFinalize
,在.Net上仍然会发生。很好的调用,Jon。查看MS不久前发布的CLI源代码,在内部
ReRegisterForFinalize()
方法中有一条
if
语句,该语句表示“如果我已被标记为已完成,请删除该标记。如果没有,请将我添加到队列中。”因此,如果您在不首先抑制的情况下调用了两次ReReg,该对象只是被添加到队列中两次。然后,suppress将位清除三次(实际上,这没有任何作用,因为位从未设置为开始!)在任何情况下,都是精彩的推测,一如既往。谢谢@Jon和dlev,这正是我所怀疑的。然而,这与我之前假设的(并且可以证明是不正确的)心智模型相反,即一种方法从终结表中添加,另一种方法从终结表中删除。这显然违反了文件规定的用法,但似乎是一种疏忽,即如果某个对象被重新注册以进行最终确定,而该对象本不应该被取消,则它永远无法被取消,我很好奇原因是什么——我假设是性能原因。sehe对mono行为的研究非常有趣,我想这就是规范中未定义行为的美妙之处!