MSDN中的Dispose()示例有误?(何时将托管引用设置为null)

12

MSDN的实例代码展示了如何实现Dispose()方法,其中将对已经释放的托管资源的引用设置为null(_resource = null),但这个操作不在if (disposing)块中执行:

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;   
    }
}
_resource = null是否应该放在这个代码块内?如果调用Dispose(false)_resource将为null,无法随后进行处理!但是,如果先前没有处理_resource,那么在对象(包括_resource成员字段)即将被垃圾收集时,在此时将其设置为null有什么必要呢?
当然,“Dispose(false)”(实际上)只会在最终化期间由运行时调用。但是,如果之前未处理_resource,则在对象(包括_resource成员字段)即将被垃圾收集时,在这一点上将其设置为null的必要性是什么?
在经过大量阅读后,似乎不需要将引用设置为空,但如果您有理由相信包含类(正在处理的类)可能不会很快被垃圾收集,则对于“重”成员对象可能是一个好主意。
知道对象处理不能保证消耗代码已将对象“释放”。处置对象可能会被保留(在集合或其他位置)用于各种目的,或者只是错误的。我可以想象一个使用集合中的对象然后处置它们的应用程序,但在集合中保留它们以供稍后的进程执行删除和记录最终状态(或类似的操作)。
因此,结论如下:
1.将对“重”成员对象的引用设置为空会释放它们进行垃圾收集,即使处理的对象没有被释放。
2.清除所有对象的引用是过度的。
3.因此,在两个方面上都不重要,位置_resource = null语句(原始问题):(A)在阅读上述内容之后才考虑使用它; (B)在MSDN示例中,它对Dispose(true)Dispose(false)执行,但后者仅在对象终止并且即将被垃圾收集时发生!
if (disposing) {
    if (_resource != null) {
        _resource.Dispose();
        _resource = null;
    }
}

这样可以将所有_resource代码放在一起。还有其他想法吗?

更多阅读:


1
它的作用不大,但也没有什么害处...实际上,我更烦恼于a:写入到“控制台”,和b:即使已经被处理并且我们没有处理它(请参见“if”中缺少的括号),仍然写入到“控制台”。 - Marc Gravell
哈哈!Console.Writeline()是示例代码。使用Trace.Writeline()会更好吗?另外,没有缺少的括号——它在if行的末尾(不符合StyleCop规范)。 - Kevin P. Rice
请再看一遍,即使 _resource 为 null 并且没有调用 .Dispose(),它仍然会写入“Object disposed.”。 我的意思是 最内层if - Marc Gravell
这里有一个相关的问题表明设置_resource = null没有意义(请参见Igor Zevaka在https://dev59.com/dXA85IYBdhLWcg3wJf4Z的答案)。 - Kevin P. Rice
@Marc 啊,是的!你说得对。然而,被处理掉的“Object”是this而不是_resource,所以我向你提交代码按照原样是正确的。 - Kevin P. Rice
1个回答

7
将其设置为null相当于从堆中删除对该位置的引用或指针。这使垃圾回收器可以通过不必要猜测就清除没有引用的任何内容。_resource是需要清理其引用的一些内部使用对象,所以假设您在close内部有一个Socket,则应关闭/处理该套接字或任何其他持久性资源。一旦被处理,您将其设置为null并删除所有引用(应该删除所有引用),以便GC可以很好地完成其工作。我的答案的第二部分是一个示例,因此重复了一些内容,但希望您能理解。

将其设置为null两次并没有什么影响。处置应在清理任何资源时为true,并且只有在它(如您所说)即将被垃圾回收时才为false。


也许它放在哪里并没有太大的区别?通过在 _resource.Dispose() 后面放置 _resource = null,代码更易于阅读/维护,因为这些相关项在一个地方(想象很多项都用了许多 = null 进行丢弃)。 - Kevin P. Rice
另外,_resource 是一个托管的资源(可能不是一个套接字)。MSDN 模式将仅将托管对象的释放放在 if (disposing) 块内。我只是想弄清楚为什么将 _resource = null 不恰当地放在 _resource.Dispose() 的下面。 - Kevin P. Rice
1
我同意你的看法,它应该放在if语句中。在大多数情况下,我建议只实现一个Dispose()方法,不要管那个(就像你发布的文章之一所示),这样可以避免你所说的歧义。 - Jesus Ramos
3
哇!恍然大悟!您让我意识到,如果没有终结器(在大多数情况下不应该有),那么就没有必要使用 Dispose(bool) !我一直专注于“这种模式”,没有意识到 IDisposable 只包含 Dispose()。任何 disposed 布尔标志都可以在 Dispose() 中实现,因此也没有理由再使用 Dispose(bool) 方法!谢谢!最有价值评论的获胜者! - Kevin P. Rice
4
在完整模式(http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx)中,Dispose() 调用 Dispose(true) 来表示释放所有资源。终结器调用 Dispose(false) 来表示仅释放非托管资源(托管引用在终结化发生时不安全使用)。如果没有非托管资源,建议不要使用终结器。因此,如果不存在非托管资源,则不需要终结器或 Dispose(bool);只需要 Dispose() 方法即可。【最佳答案通常不会给你想要的,它们会迫使你学习更多!】 - Kevin P. Rice
显示剩余4条评论

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