在Dispose时将托管资源设置为空

3

可能是重复问题:
在Dispose()中将obj = null(Nothing)有意义吗?

我知道如果这个问题被关闭为重复,但我对这个主题上的一些帖子感到有些困惑。

首先是一些背景。我有一个名为Foo的类,如下所示

public class Foo : IDisposable
{
    private Dictionary<int, string> _reallyBigDictionary = 
            new Dictionary<int, string>();

    public void Dispose()
    {
        _reallyBigDictionary = null;
    }
}

我知道 Foo 的实例有限的范围(即,我知道我们不会一直保留它)。鉴于它的实例范围有限,我不明白将 _reallyBigDictionary 设为 null 实际上如何比 dispose 更快地释放内存。我的理解是,在垃圾回收运行之前,这些对象永远不会被清除。在那时,给定的 Foo 实例的引用将无论如何为空,因此我期望 GC 无论如何都会回收该内存。

这些帖子让我相信设置成员变量为 null 没有意义:

内存泄漏问题:处理还是不处理托管资源?

销毁所有对象还是让垃圾回收器来完成工作更好?

这篇帖子让我产生了不同的想法: IDisposable 接口的正确使用方法

有人能为我澄清这一点吗?在这里真的需要实现IDisposable吗?因为我无法让自己相信这一点。


“适当使用链接”有一个非常模糊的答案,你应该阅读“任何意义”链接。总结:这样做没有意义。 - H H
3个回答

12

你不需要 IDisposable 接口;这就是原因。简而言之,遵循 Rule 1 即可:除非需要,否则不要实现 IDisposable 接口。

IDisposable 接口只有两个情况需要实现:

  • 类拥有非托管资源。
  • 类拥有托管 (IDisposable) 资源。

由于 Dictionary<int, string> 不是 IDisposable 接口,所以你的类也不应该实现它。


0
通过实现 IDisposable 接口,您可以使用 Using 语句来限定资源的使用范围。 此外,许多在底层 .NET Framework 中的类会检测到一个类是 IDisposable 并为您运行 Dispose(),从而免除您将托管子对象置空的责任。

编辑 ----

由于评论和 markdown 的缘故,我想补充一下我的答案:

在.NET Framework中,许多类都实现了IDisposable接口,例如许多通用集合类型。当您处理此类集合时,Microsoft的实现将调用您的类的Dispose方法,并检测到它实现了IDisposable接口。此外,有一个情况需要将对象的引用设置为null - 包括在Dispose方法中。如果您想要删除对对象的引用,请将引用设置为null。其他类可能希望保留对同一对象的引用,因此认为将引用设置为null是错误的观点是有缺陷的。使用Dispose方法将引用设置为null与以任何其他方式将其设置为null没有区别-可以这样说,但通过在IDisposable实现中执行此操作,您可以更加确定性和防错地确保发生这种情况。实现IDisposable也是实现作用域(例如,用于事务性算法等)的绝佳方式。实现IDisposable在许多方面都很有用,不必避免,只要您知道如何以及为什么它起作用。我总是惊讶于有多少.NET开发人员真正理解IDisposable接口、终结器、垃圾回收器和.NET最佳实践。


你有没有任何关于“在底层.NET框架中的类”示例,可以“检测到一个类是IDisposable并为您运行Dispose”? - LukeH
1
当父对象不再需要时,将对子对象的引用设置为null是不必要的,也无法帮助释放相关内存。不要这样做,也不要为此实现IDisposable。 - Antoine Aubry
当您处理此类集合时,Microsoft 实现将调用您的类的 Dispose 方法,因为它检测到它实现了 IDisposable 接口。您是否有这种集合类型的具体示例?(顺便说一句,这不是我的投票。) - LukeH
SortedDictionary<T>是一个例子(有很多) - Dean Chalk
1
@Dean:你能详细说明一下吗?你所说的对我来说毫无意义:SortedDictionary<K,V>本身并没有实现IDisposable,我也从未见过任何证据表明它“调用您的类的Dispose方法,因为它检测到它实现了IDisposable”。 - LukeH
我使用Reflector查看了SortedDictionary<T>.Remove()的源代码,确实没有调用Dispose()的语句。 - Tom Winter

-2

我认为在这种情况下不需要处理。这不像是一个将保持打开并继续使用资源的活动连接。当你使用完它后,它会自行管理。

如果你真的想确保那段代码没有内存泄漏,你可以尝试在处理方法中添加以下内容:

GC.Collect();

不是在倡导使用它,只是提供一下。正如我在回答中明确指出的那样,这不是需要任何特殊处理技术的情况。 - James Johnson

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