从终结器处理时,只读字段变为空

时间:2015-11-10 07:37:16

标签: c# .net garbage-collection

我有以下课程。现在有时锁定语句会抛出ArgumentNullException,在这种情况下,我可以在调试器中看到disposelock对象确实为空。

正如我所知,处理是错误的,我知道该方法是从Finalizer触发的。

但这怎么可能发生呢?它被定义为readonly并在创建对象时获取其值。

PS:我知道这不是一个好的模式,但它是给定代码的一部分,而我无法解释为什么它变为空

public abstract class DisposableMarshalByRefObject : MarshalByRefObject, IDisposable
{
    private readonly object disposeLock = new object();


   /// </summary>
   ~DisposableMarshalByRefObject()
   {
       Dispose(false);
   }

   /// <summary>
   /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
   /// </summary>
   public void Dispose()
   {
       Dispose(true);
       GC.SuppressFinalize(this);
   }

   protected void Dispose(bool disposing) //disposing = false,=> finalizer
   {
       lock (disposeLock) //ArgumentNull Exception !
       {
           ....
       }
   }
}           

3 个答案:

答案 0 :(得分:7)

垃圾收集上,未定义该集合的顺序:

  1. 首先收集this
  2. 收集下一个disposeLock
  3. 或者

    1. 首先收集disposeLock
    2. 收集下一个this
    3. 所以不要在int <上使用任何引用字段(结构,如boolDispose(false);等是安全的) / p>

      protected virtual void Dispose(bool disposing) {
        if (disposing) {
          // Explicit disposing: it's safe to use disposeLock 
          lock (disposeLock) {
            ...
          }
        } 
        else {
          // Garbage collection: there's no guarantee that disposeLock has not been collected
        }
      }
      

答案 1 :(得分:5)

除反思答案外,所有现有答案均为假。 GC在收集对象时不会将引用设置为null。由于GC,对象访问不会发生虚假失败。最终确定的顺序是未定义的,但所有存在的对象引用仍然存在且有效。

我对发生的事情的猜测:构造函数在字段初始化之前被中止。这离开了null字段。终结者后来发现了那样。

可以通过抛出异常或调用邪恶的Thread.Abort来中止构造函数。

  

在垃圾收集时,未定义该集合的顺序

集合的顺序不是 observable (除非通过弱引用...)。 finalization 的顺序是可观察的,但不是lock语句,因为对象在最终确定时不会失去同步的能力。

答案 2 :(得分:4)

由于您已经强调了readonly,因此对此进行了一些澄清。运行时不会阻止readonly字段的修改。无论来自C#的readonly在IL中变为initonly

例如,您可以使用反射轻松修改readonly字段:

class A
{
    private readonly int bar;

    public A()
    {
        bar = 1;
    }

    public void Foo()
    {
        Console.WriteLine(bar);
    }
}

var a = new A();

a.Foo(); // displays "1"
a.GetType().GetField("bar", BindingFlags.Instance | BindingFlags.NonPublic).SetValue(a, 2);
a.Foo(); // displays "2"

当然,这并不意味着您应该每次都为null测试这些字段,但当readonly字段变为<{1}}字段时,可能是的情况修改过(你面对其中一个)。

作为旁注。你真的需要终结者吗?我的意思是,有没有真正的非托管资源?