C# IDisposable、终结器和非托管资源的定义

C# IDisposable、终结器和非托管资源的定义,c#,.net,garbage-collection,idisposable,finalizer,C#,.net,Garbage Collection,Idisposable,Finalizer,我正在努力确保我对IDisposable的理解是正确的,还有一些事情我还不太确定 IDisposable似乎有两个目的 提供按需“关闭”托管对象的约定 提供释放托管对象持有的“非托管资源”的约定 我的困惑来自于确定哪些场景中存在“非托管资源” 假设您正在使用Microsoft提供的IDisposable实现(托管)类(例如,与数据库或套接字相关) 您如何知道它是否仅为上面的1或1&2实现了IDisposable 您是否负责确保释放内部可能持有或不持有的非托管资源?您是否应该向自己调用Instan

我正在努力确保我对IDisposable的理解是正确的,还有一些事情我还不太确定

IDisposable
似乎有两个目的

  • 提供按需“关闭”托管对象的约定
  • 提供释放托管对象持有的“非托管资源”的约定
  • 我的困惑来自于确定哪些场景中存在“非托管资源”

    假设您正在使用Microsoft提供的IDisposable实现(托管)类(例如,与数据库或套接字相关)

  • 您如何知道它是否仅为上面的11&2实现了
    IDisposable
  • 您是否负责确保释放内部可能持有或不持有的非托管资源?您是否应该向自己调用InstanceOffmsupliedClass.Dispose()的类添加终结器(这是正确的机制吗?)

  • 是的,您负责调用
    Dispose
    方法,或者最好使用
    using
    语句。如果对象正在实现
    IDisposable
    ,无论发生什么情况,您都应该处置它。

    using (var myObj = new Whatever())
    {
       // ..
    }
    
    类似于

    {
      var myObj;
      try
      {
         myObj = new Whatever();
         // ..
      } 
      finally
      {
        if (myObj != null)
        {
          ((IDisposable)myObj).Dispose();
        }
      }
    } // object scope ends here
    
    编辑:添加了try/finally,多亏了Talljoe-wow,这很难做到正确:)


    EDIT2:我不是说您应该使用第二个变体。我只是想向大家展示,“使用”是一种很好的语法糖分,对于一堆可能会变得非常混乱和难以理解的代码来说。

    您应该始终在实现IDisposable的对象上调用Dispose(除非他们明确告诉您“这是一种有用的约定,如ASP.NET MVC的HtmlHelper.BeginForm”)。您可以使用“using”语句来简化此操作。如果将类中IDisposable的引用作为成员字段保留,则应使用实现IDisposable来清理这些成员。如果您运行像FxCop这样的静态分析工具,它会告诉您同样的情况

    你不应该试图猜测接口。现在,该类可能不使用非托管资源,但下一个版本呢

  • 您如何知道它是实现IDisposable for 1还是 1和2以上
  • 第一个问题的答案是“你不需要知道”。如果您使用的是第三方代码,那么您在某种程度上是它的仁慈——当您调用Dispose时,您必须相信它正在正确地处理自己。如果您不确定或认为存在错误,您可以尝试使用Reflector()将其分解(如果可能的话),并检查它在做什么

  • 我是否有责任确保非托管资源 没有被释放吗?应该 我将添加一个终结器(会是吗 正确的机制?) 那叫 InstanceOffmsSuppliedClass.Dispose()
  • 如果您使用的是.NET2.0或更高版本,您应该很少需要为类实现终结器。终结器会给类增加开销,并且通常不会提供仅实现Dispose所需的更多功能。我想要一个关于正确处置的良好概述。在您的情况下,您可能希望在自己的Dispose()方法中调用
    instanceofmsuppliedclass.Dispose()


    归根结底,对对象调用Dispose()是一种很好的做法,因为它明确地让GC知道您已使用完资源,并允许用户立即清理它,并通过让其他程序员知道此时已使用完资源来间接地记录代码。但是,即使您忘记显式地调用它,它最终也会在对象无根时发生(.Net毕竟是一个托管平台)。只有当对象具有需要隐式清理的非托管资源时(即,使用者可能忘记清理它,并且这会有问题),才应实现终结器

    您对对象的内容不负责。Dispose()应该是透明的,并且释放它需要释放的内容。在那之后,你就没有责任了

    非托管资源是像您将在(托管)C++中创建的资源,在这里,您通过指针和“新”语句分配内存,而不是“GCNEX”语句。当你在C++中创建一个类时,你负责删除这个内存,因为它是本地内存,或者是非托管的,垃圾回收器不关心它。您还可以通过封送分配和不安全代码来创建非托管内存


    使用托管C++时,您也不必手动实现IDISPOSIGLE类。当您编写解构器时,它将被编译为Dispose()函数。

    为什么它对您很重要

    如果可能的话,我将一次性对象的范围包装在一个容器中。这将在使用结束时调用dispose。如果不需要,则在不再需要该对象时显式调用dispose


    无论是原因1还是原因2都不是必需的。

    如果所讨论的类是Microsoft提供的(即数据库等),那么处理Dispose(来自IDisposable)很可能已经得到了处理,这取决于您如何调用它。例如,使用数据库的标准做法如下所示:

    //...
    using (IDataReader dataRead = new DataReaderObject())
    {
       //Call database
    }
    
    这与写作基本相同:

    IDataReader dataRead = null;
    try
    {
        dataRead = new DataReaderObject()
        //Call database
    }
    finally
    {
        if(dataRead != null)
        {
            dataRead.Dispose();
        }
    }
    
    据我所知,在从IDisposable继承的对象上使用前者通常是一种良好的做法,因为它将确保适当释放资源

    至于自己使用IDisposable,实现取决于您。一旦从中继承,您应该确保该方法包含处理手动创建的任何DB连接所需的代码,释放可能保留的资源或防止对象被破坏,或者只清理大型资源池(如