请关于C#IDisposable的一些说明

时间:2011-08-25 08:35:30

标签: c#

我在不同的主题和不同的论坛中已经看过很多次代码。特别是我从How does GC and IDispose work in C#?获得了这个。

class MyClass : IDisposable
{
    ...

    ~MyClass()
    { 
        this.Dispose(false);
    }

    public void Dispose()
    {
        this.Dispose(true);
       GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        { /* dispose managed stuff also */ }

        /* but dispose unmanaged stuff always */
    }
}

我的问题是:

  1. 是否有必要创建一个显式的析构函数?该类继承自IDisposable,在GC清理期间,最终将执行Dispose()。

  2. Dispose(bool disposing)中参数'disposing'的意义是什么?为什么有必要区分托管和非托管对象的处理?

5 个答案:

答案 0 :(得分:5)

垃圾收集器本身对IDisposable一无所知。它不会为你配置任何东西 - 它所要做的只是调用终结器。

使用“disposing”参数进行重载的重点是终结器将调用Dispose(false)来指示它已从终结器调用,并且托管对象不需要任何清理,而如果你明确调用Dispose(例如通过using语句),最终会调用Dispose(true)

这种模式的部分要点是它对于派生类是可扩展的 - 只有基类终结器需要调用Dispose,其他一切都会在必要时通过覆盖Dispose(bool)来支持它

但是,除非您实际上可以直接访问非托管资源 - 或者期望派生类 - 否则您可能根本不需要终结器。如果你需要相当直接的访问权限,那么SafeHandle有助于避免编写终结器。你应该almost never need to write a finalizer these days。我个人很少自己实现IDisposable,当我这样做时,通常来自一个密封的类(因为我喜欢在可能的情况下密封类) - 并且它几乎从不涉及终结器......所以我只是编写一个Dispose方法来实现接口并将其留在那里。更简单。

full advice for implementing IDisposable in every situation you can imagine非常啰嗦和复杂。我认为尽可能将自己局限于更简单的情况是值得的。

答案 1 :(得分:3)

  

1是否有必要创建一个显式的析构函数?

只有在极少数情况下直接拥有非托管资源。

  

该类继承自IDisposable,在GC清理期间,最终将执行Dispose()。

IDisposable接口仅允许在using(){}块中使用。 GC最终将调用Dispose(),但这将(太晚)。请注意,它将使用disposing==false并且只尝试清理非托管内容。你很可能没有。

  

2 Dispose(bool disposing)中参数'disposing'的意义是什么?为什么有必要区分托管和托管对象的处理?

因为在您处置时不需要Dispose()托管资源。 GC的算法可确保您的托管资源已经自己进行GC管理。调用他们的Dispose()最多是无害的。

请注意,此代码基于标准实现模式。如果省略析构函数,重载Dispose(bool)的唯一原因是可能的继承。

较短的版本,请注意sealed

sealed class MyClass : IDisposable
{
   private FileStream MyManagedResource;

   public void Dispose()
   {
       //this.Dispose(true);
       //GC.SuppressFinalize(this);

       /* dispose managed stuff  */
       if (MyManagedResource != null)
          MyManagedResource.Dispose();  // this is why we do it all
   }

   // ~MyClass() { }

}

答案 2 :(得分:2)

1:没有;这只在直接包装外部非托管资源的类中很常见,例如windows-handles;或者如果某些子类可能会这样做。如果您只处理托管对象,添加终结器实际上是一个的东西,因为它影响了wat收集工作

2:它告诉Dispose(bool)代码它是否在垃圾收集中是最新的(如果是false)。收集时,您不应该触摸任何自己以外的其他对象,因为它们可能已经消失了。但是,如果您被选择性地处理(即true),您可能希望清理一些封装的托管对象;在要包装的连接上调用.Close(),例如

答案 3 :(得分:1)

  1. 不,您不需要实现析构函数。实际上,微软自己强烈建议不要这样做。
  2. disposing用于识别Dispose方法的确切调用方。

答案 4 :(得分:0)

析构函数是GC清理Dispose期间执行的原因,正如您所提到的那样。因此,如果程序员之前没有明确地处理它,那么你需要确保该对象最终被处理掉。

IDisposable存在的原因是将非托管资源返回给系统。这就是你总是“处理非托管的东西”的原因:当程序员明确调用Dispose和执行终结器时,应该处理非托管资源(但请注意无参数Dispose方法将明确阻止执行终结器 - 这可以防止资源的双重处理,也有利于提高性能。

disposing参数用于区分显式Dispose(由程序员)和在终结器内触发的资源的隐式处理。如果您的类具有类型IDisposable的成员,那么处置您的对象的可能性也很可能也会丢弃那些其他成员对象,因此代码也会运行“dispose managed stuff”分支。另一方面,如果你没有显式地处理该对象(并且它是由GC运行的终结器,那么这样做)那么这些其他对象可能已经被垃圾收集了。如果是这种情况,你不会想碰它们。