如何在C#中检查对象是否已被释放

103

可能是重复问题:
如何检查IDisposable对象引用是否已被释放?

除了判断对象是否被释放的方法之外,还有其他方法吗?

try
{
    myObj.CallRandomMethod();
} catch (ObjectDisposedException e)
{
    // now I know object has been disposed
}

在我的情况下,我正在使用TcpClient类,它具有Close()方法,该方法会处理对象,而这可能发生在我无法控制的代码块中。在这种情况下,我希望拥有比捕获异常更好的解决方案。


3
如何判断一个 IDisposable 对象引用是否已被释放?答案:使用对象的 Dispose() 方法时,可以将对象从内存中释放,但在尝试再次使用已释放的对象时会导致 ObjectDisposedException 异常。因此,可以通过捕获该异常来检查对象是否已被释放。另外,可以创建一个标志变量以指示对象是否已被释放,当调用 Dispose() 方法时将其设置为 true。在以后的代码中,检查此标志以确定对象是否可用。 - Iain Ward
我知道这个问题通常太普遍了,以至于在stackoverflow上没有相关的问答,但是我却搜索失败了。 - jethro
4个回答

57

可靠的解决方案是捕获ObjectDisposedException异常。

编写覆盖Dispose方法的实现并不起作用,因为调用Dispose方法的线程和访问对象的线程之间存在竞态条件:在检查假设的IsDisposed属性后,对象可能已经被真正释放,仍然会抛出异常。

另一种方法是公开一个假设的事件Disposed(像一样),用于通知所有感兴趣的对象有关正在处理的对象的信息,但根据软件设计的不同,这可能很难计划。


8
异常处理应该用来处理异常情况,而不是成为代码基础设施的一部分。过度使用异常是懒惰行为,这也不是异常处理的本意,这就是为什么原帖明确要求寻找可靠的替代方案。 - user236800
我们应该责怪TcpSocket的实现者抛出不必要的异常吗?DisposedException是异常情况;问题不应该是"处理异常",因为代码已经这样做了,而且你必须接受它。无论如何。 - Luca

45

一个好的方法是从TcpClient派生并重写Disposing(bool)方法:

class MyClient : TcpClient {
    public bool IsDead { get; set; }
    protected override void Dispose(bool disposing) {
        IsDead = true;
        base.Dispose(disposing);
    }
}

如果其他代码创建了实例,那么这种方法将不起作用。然后你将不得不做一些绝望的事情,比如使用反射来获取私有变量 m_CleanedUp 的值。或者捕获异常。

坦白地说,这些方法都不太可能有一个很好的结果。你确实想要写入TCP端口。但是你不会成功,因为那些有缺陷的、无法控制的代码现在正在控制着你的代码。你已经增加了错误的影响范围。与那些代码的所有者谈判并解决问题是远远最好的解决方案。

编辑:反射示例:

using System.Reflection;
public static bool SocketIsDisposed(Socket s)
{
   BindingFlags bfIsDisposed = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetProperty;
   // Retrieve a FieldInfo instance corresponding to the field
   PropertyInfo field = s.GetType().GetProperty("CleanedUp", bfIsDisposed);
   // Retrieve the value of the field, and cast as necessary
   return (bool)field.GetValue(s, null);
}

2
DLL文件中的类怎么样?! - Hamed Zakery Miab
public bool IsDead { get; private set; } - mpiliszcz
public bool IsDead { get; private set; } - undefined

19
如果您不确定对象是否已被处理,请调用Dispose方法本身,而不是诸如Close之类的方法。虽然框架不能保证即使对象以前已被处理,Dispose方法也必须无异常地运行,但这是一种常见模式,并且据我所知在框架中所有可处理对象上都实现了该模式。
根据Microsoft,Dispose的典型模式如下:
public void Dispose() 
{
    Dispose(true);

    // Use SupressFinalize in case a subclass
    // of this type implements a finalizer.
    GC.SuppressFinalize(this);      
}

protected virtual void Dispose(bool disposing)
{
    // If you need thread safety, use a lock around these 
    // operations, as well as in your methods that use the resource.
    if (!_disposed)
    {
        if (disposing) {
            if (_resource != null)
                _resource.Dispose();
                Console.WriteLine("Object disposed.");
        }

        // Indicate that the instance has been disposed.
        _resource = null;
        _disposed = true;   
    }
}

注意检查_disposed。如果您调用实现此模式的Dispose方法,则可以多次调用Dispose而不会遇到异常。

2
这并不有帮助。该成员是私有的。 - Hans Passant
抱歉,也许我误读了你的问题。你是想检测一个对象是否被处理掉,但不是出于“我应该处理掉这个对象”的原因吗?如果是这样,为什么?一个未知代码可能处理掉对象看起来像是一种泄漏的设计。 - Ryan Brunner
4
虽然框架本身不提供任何保证,但IDisposable的文档中说明:“如果一个对象的Dispose方法被调用多次,那么对象必须忽略除第一次外的所有调用。如果对象的Dispose方法被多次调用,则该对象不能抛出异常。当资源已被释放时,除了Dispose之外的实例方法可以抛出ObjectDisposedException。” http://msdn.microsoft.com/en-us/library/system.idisposable.dispose.aspx - LukeH
1
如果一个对象实现了 IDisposable 接口,那么无论该对象是否有公共成员 (Dispose),都可以调用 (IDisposable.Dispose) - supercat

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接