【问题标题】:What C# class objects acquire unmanaged resources? Is there a list?哪些 C# 类对象获取非托管资源?有清单吗?
【发布时间】:2013-04-04 03:11:50
【问题描述】:

我正在使用 XML 对类对象进行序列化和反序列化时,我偶然发现了这篇博文,该博文展示了如何在 Windows Phone 7 上使用隔离存储区域进行此操作。 Windows Phone 7 是我正在开发的平台:

this example 中,他明确调用Dispose() 的唯一对象是TextReader 对象。我在MSDN上查了TextReader对象,发现the documentation是这么说的:

释放 TextReader 使用的非托管资源,并可选择释放托管资源。

所以我认为他这样做的原因是立即释放 TextReader 对象获取的非托管资源。如果不是因为他的博客文章,我不会想到这样做。显然,我不想开始对所有可见的对象调用 Dispose(),那么对于至少调查特定对象何时应该调用 Dispose() 而言,有什么好的经验法则?是否有一些指导方针或某个地方的列表,至少是需要这种特殊处理的流行 .NET 对象?

【问题讨论】:

    标签: c# .net dispose unmanaged textreader


    【解决方案1】:

    显然我不想开始对

    中的每个对象调用 Dispose()

    错了。

    一般来说,任何实现了IDisposable 的对象都应该在完成后立即释放,通常使用using 语句。

    大多数没有非托管资源的对象都没有实现IDisposable(也没有Dispose()方法),因此您无需担心。

    唯一的例外是实现IDisposable 的基类,以防某些派生实现需要处理(例如,IEnumeratorComponentTextReader)。
    然而,哪些具体的实现需要被释放并不总是很明显(而且它可能随时改变),所以无论如何你都应该释放它们。

    【讨论】:

    • 即使它只是“残留的”,除非您检查源代码,否则您无法知道这一点。实现IDisposable 是与调用者的合同,他们将确保对象在完成后被释放。实现细节可能会改变,但你不能做出这些假设。
    • 为了安全起见,如果它有 Dispose 方法,请调用它。当您对它感到更舒服时,您可以了解哪些东西“真的”不需要处理。例如,不要费心处理 Task 对象。处理内存中的 IO 对象(例如 StringWriter)并不重要。特定类/方法的文档应指定所有这些。但可以谨慎行事并处理所有内容。
    • @Robert 不太确定该决定背后的历史/理由。对我来说,C++ 中使用的RAII idiom(在保证对析构函数的调用方面确实比名称所暗示的更重要)更有意义。但我敢肯定有人已经考虑到了这一点,因为 RAII 已经是一种成熟的模式。
    • @Jacob 不确定我是否同意。您仍然依赖于可以轻松地从一个框架版本更改为另一个版本的实现细节。我们不要自欺欺人,没有人会检查每次更新的所有代码。我们只是交叉手指并确保它编译。关键是,调用 Dispose() 并没有真正的伤害
    • @CodyGray 特别是对于 Task 案例,这是有危害的,即如果您尝试这样做,您可能会弄错(这可能会导致完全独立的代码中出现错误),并得到它正确需要丑陋、丑陋的代码。见blogs.msdn.com/b/pfxteam/archive/2012/03/25/10287435.aspx。对于 StringWriter 示例,我可以理解您的观点,但很明显,像这样的类不会获取非托管资源。
    【解决方案2】:

    显然我不想开始对所有可见的对象调用 Dispose(),那么对于至少调查特定对象何时应该调用 Dispose() 来说,有什么好的经验法则?

    这不是问题。编译器不会让你在没有实现它的对象上调用 Dispose()。

    应该为每个确实实现它的对象调用 Dispose()(它将通过IDisposable 接口实现)。这是您应该遵循的准则。事实上,当一个对象实现IDisposable意味着:它有需要释放的非托管资源。

    如果您将对象的创建和使用简单地包装在 using 语句中,这将变得不那么繁琐,例如:

    using (DisposableObject obj = new DisposableObject(...))
    {
        obj.DoWork();
    } // obj.Dispose() is automatically called here, even if an exception is thrown
    

    【讨论】:

    • 谢谢!使用的副作用正是我想要的。
    • +1。 @RobertOschler,只是想知道从您的角度来看using 的目标是什么? (如果Dispose 是副作用,那应该是真正的目标......)
    • @AlexeiLevenkov - 我认为“使用”是为了别名,从而简化代码中的对象链?我不知道它也可以作为对象生命周期的范围边界。
    • @RobertOschler,我明白了。谢谢...我没有意识到“您需要以这种方式编写代码”会导致这种假设。以后需要回复使用的时候会记在心上的。
    【解决方案3】:

    实际上,您确实必须处理实现 IDisposable 的对象。

    与直接调用 Dispose() 不同的标准方法是:

    using(AnyIDisposable obj = ...)
    {
        // work with obj here
    }
    //The Dispose() method is already called here
    

    【讨论】:

      【解决方案4】:

      如果我错了,请纠正我。 据我阅读/理解,.NET Framework 的所有类都是托管的(在程序员看来,虽然底层它们可能使用非托管代码),所以理论上你不需要调用 Dispose() 或使用,因为 gc 会小心。但有时非常推荐使用它们,请参阅IDisposable InterfaceWhich managed classes in .NET Framework allocate (or use) unmanaged memory?http://blogs.msdn.com/b/kimhamil/archive/2008/11/05/when-to-call-dispose.aspx

      编辑:(你是对的菜鸟)为澄清起见,我将添加来自 IDisposable Interface 的 Nayan 答案

      建议调用 dispose 或 using,当:

      1.你的类有很多对象,并且有很多交叉引用。即使全部托管,GC也可能无法回收 由于活动引用而产生的内存。你有机会(除了 编写终结器)以解开引用并分解链接 你连接它们的方式。因此,您正在帮助 GC 回收 记忆。

      2.您打开了一些流,这些流一直存在,直到类的对象死亡。即使文件/网络等的这种实现是 管理,它们深入到 Win32 模式下的句柄。因此,你得到一个 有机会编写一个 Dispose 方法,您可以在其中关闭流。这 GDI 对象也是如此,还有更多。

      3.您正在编写一个使用非托管资源的类,并且您希望将您的程序集发送给第三方。你最好用一次性的 模式以确保您能够释放手柄以避免 泄漏。

      4.您的类实现了许多事件处理程序并将它们与事件挂钩。类的对象 暴露事件,如 Form 等,不会被 GC 释放,因为 您的班级(可能)本地的实现仍然挂钩 那些事件。您可以在 Dispose 中解开这些事件处理程序;再次 帮助 GC。

      【讨论】:

      • 我认为IDisposable 最好的总结是“你的班级已经要求外部实体代表它做事,这可能会损害其他实体,并且需要让以前的实体知道他们的不再需要服务”。链接中的所有四个项目都满足该标准(尽管 #1 仅在许多项目包含相互引用的情况下;Dispose 的工作是清除 to 正在处理的对象的引用)除此之外还有更多。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-06
      • 2012-03-28
      • 2013-02-02
      相关资源
      最近更新 更多