从终结器处理时,只读字段变为空
Readonly fields becomes null when disposing from finalizer
我有以下 class。现在有时 lock 语句会抛出 ArgumentNullException
,在这种情况下,我可以在调试器中看到 disposelock
对象确实为空。
正如我所看到的,disposing 是 false,我知道该方法是从 Finalizer 触发的。
但这怎么会发生呢?它被定义为只读,并在创建对象时获取其值。
PS:我知道这不是一个好的模式,但它是给定代码的一部分,我只是无法解释为什么它会变成 null
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 !
{
....
}
}
}
关于垃圾collectioncollection的顺序未定义:
- 第
this
个收藏
- 下一篇
disposeLock
收藏
或
- 第
disposeLock
集
- 下一篇
this
收藏
所以不要使用任何 reference 字段(structs like int
, bool
等是安全的) 在 Dispose(false);
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
}
}
既然您强调了 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
字段被修改(您遇到过其中一个)。
作为旁注。你真的需要终结器吗?我的意思是,是否有任何 true 非托管资源?
除反思答案外,所有现有答案均为错误。 GC 在收集对象时不会将引用设置为 null。对象访问不会因 GC 而虚假地失败。最终确定的顺序未定义,但所有存在的对象引用将继续存在并且有效。
我对发生的事情的猜测:构造函数在字段初始化之前中止。那离开了字段 null
。终结器后来发现它是这样的。
可以通过抛出异常或调用 Thread.Abort
来中止构造函数,这是邪恶的。
On garbage collection the order of that collection is not defined
collection 的顺序不是 observable(除非通过弱引用...)。 finalization 的顺序是可以观察到的,但不能用 lock
语句,因为对象在完成时不会失去同步的能力。
我有以下 class。现在有时 lock 语句会抛出 ArgumentNullException
,在这种情况下,我可以在调试器中看到 disposelock
对象确实为空。
正如我所看到的,disposing 是 false,我知道该方法是从 Finalizer 触发的。
但这怎么会发生呢?它被定义为只读,并在创建对象时获取其值。
PS:我知道这不是一个好的模式,但它是给定代码的一部分,我只是无法解释为什么它会变成 null
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 !
{
....
}
}
}
关于垃圾collectioncollection的顺序未定义:
- 第
this
个收藏 - 下一篇
disposeLock
收藏
或
- 第
disposeLock
集 - 下一篇
this
收藏
所以不要使用任何 reference 字段(structs like int
, bool
等是安全的) 在 Dispose(false);
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
}
}
既然您强调了 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
字段被修改(您遇到过其中一个)。
作为旁注。你真的需要终结器吗?我的意思是,是否有任何 true 非托管资源?
除反思答案外,所有现有答案均为错误。 GC 在收集对象时不会将引用设置为 null。对象访问不会因 GC 而虚假地失败。最终确定的顺序未定义,但所有存在的对象引用将继续存在并且有效。
我对发生的事情的猜测:构造函数在字段初始化之前中止。那离开了字段 null
。终结器后来发现它是这样的。
可以通过抛出异常或调用 Thread.Abort
来中止构造函数,这是邪恶的。
On garbage collection the order of that collection is not defined
collection 的顺序不是 observable(除非通过弱引用...)。 finalization 的顺序是可以观察到的,但不能用 lock
语句,因为对象在完成时不会失去同步的能力。