我有以下课程。现在有时锁定语句会抛出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 !
{
....
}
}
}
答案 0 :(得分:7)
在垃圾收集上,未定义该集合的顺序:
this
disposeLock
或者
disposeLock
this
所以不要在int
<上使用任何引用字段(结构,如bool
,Dispose(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}}字段时,可能是的情况修改过(你面对其中一个)。
作为旁注。你真的需要终结者吗?我的意思是,有没有真正的非托管资源?