static tc gto;public class tc
{
public int a=99;~tc()
{
a=-1; //set breakpoint 1
gto=this;
}
}private void button1_Click(object sender,EventArgs e)
{
tc to=new tc();
GC.Collect();
GC.WaitForPendingFinalizers();
return; // set breakpoint 2
}two problems:
click button once,it seem the ~tc() not execute at all.and it will excute at the second time clicking.why?
the second clicking,breakpoint 2, "gto" and "to", gto.a=-1,while to.a=99,why?

解决方案 »

  1.   

    1,在调试时,Visual Studio提供了显示即时值的功能,比如让你的breakpoint 2的地方,能够检查tc的值。
    要检查tc的值,这就要求tc不能被垃圾回收,为此Visual Studio调试器插入了特殊代码以保证tc在离开button1_Click()以前不会被回收。2,Release版就不同了。你把例子用Release编译运行,就会发现,同Debug相反,~tc()在第一次GC.Collect时就被调用了。3,你的第二个疑问,则这样解释:第二次点击button1_Click的时候,调试器不再需要保留第一个tc实例,于是调用了tc的finalizer()。
    设置a=-1,并用gto保存了第一个tc的实例。第二个tc实例则没有被尝试回收(见第一点解释),它的a当然还是99。4,在finalizer()中把自己赋值给gto,叫做复活。但由于已经调用过一次finalizer()了,以后回收被复活的实例时,
    将不会再次调用它的finalizer(),除非在finalizer中显示地调用了GC.ReRegisterForFinalize(this)。
      

  2.   

    Effective C#里面有更加详细的说明。基本上不是.NET本身的问题。
      

  3.   

    不是.net本书的问题?也就是说不是垃圾回收的问题,你确定?
      

  4.   

    不知道你用的是.net哪个版本的。我的.net2.0没有你说的第一种情况。
    我的解释如下:
    对象析构并不是垃圾回收,只不过会在垃圾回收之前调用,垃圾回收是由GC决定的。
    你的程序:
    第一次点击:to指向一个一个临时的tc对象,我们叫他tc1对象。然后,在断点2处返回时,临时变量to无效,tc1由于缺少被引用而被执行对象的析构,在析构中把tc1.a设置为1,但是由于第二步全局静态变量gto保留了一个对tc1的引用,这块内存实际上并没有释放,仍然由程序保留使用。你可以再做一个按钮,你会发现gto.a仍有可以使用。
    我的是.net2.0,没有出现你说的不执行那种情况,只是有时候会滞后一点而已。第2次点击时:to指向一个新的tc2临时对象,而gto还是指向的老tc1对象,所以在断点2处会有不同。很明显,第2次点击后,tc2调用析构时,tc1由于缺乏引用会被GC真正的回收。
      

  5.   

    呵呵,顺便说一下,我是在析构函数里加里一个MessageBox来确定的。在Release下也是如此。
      

  6.   

    刚才试了试,在debug下确实第一次没有引发析构。学习中。
      

  7.   

    真理真是难以发现的,gomoku说的对吗?
      

  8.   

    你说对了一半,不对的地方是不是因为调试保存了局部变量的值而是debug下没有优化,release下进行了优化而已,如果在release下你在
    GC.Collect(); 
    GC.WaitForPendingFinalizers(); 
    后面加入
    string s = to.ToString();他的运行结果就会和debug下的效果相同