【问题标题】:Question on IDisposable关于 IDisposable 的问题
【发布时间】:2010-10-28 11:43:22
【问题描述】:

我的问题是为什么我需要 IDisposable?我有一个消耗一些资源的类 需要释放。我有两个选择

没有 IDisposable

class SomeUtilityClass
{
 public Stop()
 {
   // free resources
 }
}

使用 IDisposable

    class SomeUtilityClass, IDisposable
    {
     public void Dispose()
     {
       // free resources
     }
    }

那么为什么我需要 IDisposable 呢?如何命名函数并不重要。

class Program
{

 public Main(..)
 {
   SomeUtilityClass _class = new SomeUtilityClass();

   // clean up when we've done

  _class.Stop();

   // OR
  _class.Dispose();

 }
}

【问题讨论】:

标签: c# .net idisposable


【解决方案1】:

因为 C# 支持 IDisposable 并且你可以使用很酷的using 语法:

using (IDisposable someDisposable = new ...)
{
    // your code with someDisposable 
}

这实际上是由编译器转换成这样的:

IDisposable someDisposable = new ...
IDisposable someDisposable2 = someDisposable ;
try
{
    // your code with someDisposable 
}
finally
{
    if (someDisposable2 != null)
    {
        someDisposable2.Dispose();
    }
}

因此,如果在 using 块内发生任何异常,您的对象无论如何都会被释放。

【讨论】:

  • 所以使用 IDisposable 的唯一原因是'以防万一你要使用“使用”'
  • 使用的主要优点是它使用 try/finally 块包装您的代码,因此如果发生异常,您的所有资源将被正确释放。
  • 对,所以可以说引入 IDisposable 只是为了引入“使用”包装?
  • 是的,也是为了创建所有 c# 开发人员使用的通用模式。
【解决方案2】:

只有当你的类消耗非托管资源并且需要立即释放它们(流、数据库等)时,你才应该真正使用 IDisposable。

当存在导致线程被卸载的未处理异常时,它还为 CLR 提供了一种“清理”方法。

调用 IDisposable 会将对象标记为可立即用于垃圾收集,但如果未正确实现,您可能会导致对象被提升为垃圾收集生成,这可能会导致内存压力(如果需要,请参阅 Jeffery Richters CLR via c# 3完整的解释)。

一个快速的谷歌打开了这个: http://kleahy-technical.blogspot.com/2009/01/idisposable-and-garbage-collection.html

我建议您阅读 IDisposable 模式,何时使用它,何时不使用它以及它对 GC 和状态的影响。

编辑: stackoverflow 上也有大量信息: Use of Garbage Collection?

【讨论】:

    【解决方案3】:

    在您的情况下,实施 IDisposable 没有多大意义,因为您可以手动处置您的资源。

    IDisposable 的一个常见用途是当您公开一个处理数据连接的接口,并且您希望所有派生类在完成后释放它们。

    考虑一下:

    public interface IDataCtx
    {
       void CallDB();
    }
    
    public class MyDataCtx : IDataCtx
    { 
       private SqlConnection dc;
    
       public MyDataCtx() { dc = new SqlConnection(); dc.Open(); }
    
       public void CallDB();
       {
           dc.Something();
       }
    }
    

    允许你做这样的事情:

    IDataCtx ctx = new MyDataCtx();
    ctx.CallDB();
    

    但是等等,那个打开的连接呢?哦哦!

    如果您创建了IDataCtx : IDisposable(并在派生的 ctx 中实现了代码),您可以这样做:

    IDataCtx ctx;
    using (ctx = new MyDataCtx())
    {
       ctx.CallDB();
    }
    

    保证无论您使用什么 IDataCtx 实现,它都会被处理掉(即使在出现异常的情况下)。

    反正我就是这么用的。 (而且这只是一种很好的做法)。

    【讨论】:

    • 感谢您的示例代码。但这需要我使用“使用”,否则保证无效?
    • 正确。你当然可以做 try/catch/finally(并明确地在 finally 中处理),但这是更好的做法。
    【解决方案4】:

    IDisposableusing 关键字交互,以便在您自己之后轻松清理,例如:

    using (var file = new FileStream(...))
    {
        file.Write(...);
    }
    

    在上面的代码中,FileStream 在 using 块完成后立即关闭,而不是等待被垃圾回收。

    【讨论】:

      【解决方案5】:

      这是 C# 语言中使用的约定。

      您还可以使用漂亮的 using 声明。

      using (SomeUtilityClass _class = new SomeUtilityClass()) {
      
      } // Dispose is automatically called
      

      【讨论】:

        【解决方案6】:

        除了能够使用 using 语句外,它还给垃圾收集器一个提示,表明可以从内存中删除对象。

        【讨论】:

        • 你说的是public void Dispose()吗?
        • 实现 IDisposable 的类会将程序集标记为由 GC 清除,而不仅仅是功能
        【解决方案7】:

        如果您的类拥有非托管资源或您的类拥有托管 IDisposable 资源,则您通常应该实现 IDisposable 接口。

        关于何时实施 IDisposable 和 Finalizer 的易读小文章可在此处找到:http://nitoprograms.blogspot.com/2009/08/how-to-implement-idisposable-and.html

        【讨论】:

        • 那篇文章省略了 iDisposabe 的一个关键用例:订阅来自长寿对象的事件的对象。当您的对象不再有用时,您添加到寿命更长的对象的任何委托都必须被删除,否则您的对象的生命周期 - 以及您的对象持有的任何其他引用 - 将与寿命更长的物体。
        猜你喜欢
        • 1970-01-01
        • 2023-04-07
        • 2021-11-29
        • 2011-09-22
        • 2018-05-23
        • 2011-11-16
        • 2021-03-27
        • 2017-01-27
        • 2011-10-31
        相关资源
        最近更新 更多