IDisposable、Finalizers 和非托管资源的定义

发布于 2024-07-25 11:36:29 字数 491 浏览 9 评论 0 原文

我试图确保我对 IDisposable 的理解是正确的,但有些事情我仍然不太确定。

IDisposable 似乎有两个目的。

  1. 提供按需“关闭”托管对象的约定。
  2. 提供释放托管对象所持有的“非托管资源”的约定。

我的困惑来自于确定哪些场景有“非托管资源”在发挥作用。

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

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

I'm trying to make sure that my understanding of IDisposable is correct and there's something I'm still not quite sure on.

IDisposable seems to serve two purpose.

  1. To provide a convention to "shut down" a managed object on demand.
  2. To provide a convention to free "unmanaged resources" held by a managed object.

My confusion comes from identifying which scenarios have "unmanaged resources" in play.

Say you are using a Microsoft-supplied IDisposable-implementing (managed) class (say, database or socket-related).

  1. How do you know whether it is implementing IDisposable for just 1 or 1&2 above?
  2. Are you responsible for making sure that unmanaged resources it may or may not hold internally are freed? Should you be adding a finalizer (would that be the right mechanism?) to your own class that calls instanceOfMsSuppliedClass.Dispose()?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

花开雨落又逢春i 2024-08-01 11:36:29
  1. 你怎么知道它是否只实现了 IDisposable 1 或
    上面的1和2?

你的第一个问题的答案是“你不需要知道”。 如果您使用第三方代码,那么您在某种程度上会受到它的仁慈 - 当您调用 Dispose 时,您必须相信它会正确处理自身。 如果您不确定或者认为存在错误,您可以随时尝试使用 Reflector() 来反汇编它(如果可能的话)并检查它在做什么。

  • 我是否有责任确保非托管资源可能或可能
    内部不持有被释放? 应该
    我要添加一个终结器(那是
    正确的机制?)到我自己的班级
    那个叫
    instanceOfMsSupplierClass.Dispose()?
  • 如果您使用 .Net 2.0 或更高版本,您应该很少(如果有的话)需要为您的类实现终结器。 终结器会增加类的开销,并且通常不会提供比仅实现 Dispose 所需的更多功能。 我强烈建议访问这篇文章,以获取有关正确处置的良好概述。 在您的情况下,您需要在自己的 Dispose() 方法中调用 instanceofMSSuppliedClass.Dispose()

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

    1. How do you know whether it is implementing IDisposable for just 1 or
      1&2 above?

    The answer to your first question is "you shouldn't need to know". If you're using third party code, then you are its mercy to some point - you'd have to trust that it's disposing of itself properly when you call Dispose on it. If you're unsure or you think there's a bug, you could always try using Reflector() to disassemble it (if possible) and check out what it's doing.

    1. Am I responsible for making sure that unmanaged resources it may or may
      not hold internally are freed? Should
      I be adding a finalizer (would that be
      the right mechanism?) to my own class
      that calls
      instanceOfMsSuppliedClass.Dispose()?

    You should rarely, if ever, need to implement a finalizer for your classes if you're using .Net 2.0 or above. Finalizers add overhead to your class, and usually provide no more functionality then you'd need with just implementing Dispose. I would highly recommend visiting this article for a good overview on disposing properly. In your case, you would want to call instanceofMSSuppliedClass.Dispose() in your own Dispose() method.

    Ultimately, calling Dispose() on an object is good practice, because it explictly lets the GC know that you're done with the resource and allows the user the ability to clean it up immediately, and indirectly documents the code by letting other programmers know that the object is done with the resources at that point. But even if you forget to call it explicitly, it will happen eventually when the object is unrooted (.Net is a managed platform after all). Finalizers should only be implemented if your object has unmanaged resources that will need implicit cleanup (i.e. there is a chance the consumer can forget to clean it up, and that this will be problematic).

    黄昏下泛黄的笔记 2024-08-01 11:36:29

    您应该始终对实现 IDisposable 的对象调用 Dispose(除非他们明确告诉您这是一个有用的约定,例如 ASP.NET MVC 的 HtmlHelper.BeginForm)。 您可以使用“using”语句来简化此操作。 如果您将类中 IDisposable 的引用作为成员字段,那么您应该使用 一次性模式来清理这些成员。 如果您运行 FxCop 这样的静态分析工具,它也会告诉您同样的信息。

    您不应该试图对界面进行事后猜测。 今天该类可能不使用非托管资源,但下一个版本又如何呢?

    You should always call Dispose on objects that implement IDisposable (unless they specifically tell you' it's a helpful convention, like ASP.NET MVC's HtmlHelper.BeginForm). You can use the "using" statement to make this easy. If you hang on to a reference of an IDisposable in your class as a member field then you should implement IDisposable using the Disposable Pattern to clean up those members. If you run a static-analysis tool like FxCop it will tell you the same.

    You shouldn't be trying to second-guess the interface. Today that class might not use an unmanaged resource but what about the next version?

    卷耳 2024-08-01 11:36:29

    您不对对象的内容负责。 Dispose() 应该是透明的,并释放它需要释放的内容。 以后你就不用负责了。

    非托管资源是类似于您在(托管)C++ 中创建的资源,其中通过指针和“new”语句而不是“gcnew”语句分配内存。 当您在 C++ 中创建类时,您有责任删除此内存,因为它是本机内存或非托管内存,并且垃圾收集器不关心它。 您还可以通过元帅分配创建此非托管内存,并且我认为是不安全的代码。

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

    You're not responsible for the contents of an object. Dispose() should be transparent, and free what it needs to free. After that, you're not responsible for it.

    Unmanaged resources are resources like you would create in (managed) C++, where you allocate memory through pointers and "new" statements, rather than "gcnew" statements. When you're creating a class in C++, you're responsible for deleting this memory, since it is native memory, or unmanaged, and the garbage collector does not about it. You can also create this unmanaged memory through Marshal allocations, and, i'd assume, unsafe code.

    When using Managed C++, you don't have to manually implement the IDisposable class either. When you write your deconstructor, it will be compiled to a Dispose() function.

    甜中书 2024-08-01 11:36:29

    如果相关类是 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,具体的实现取决于你。 一旦你继承它,你应该确保该方法包含处理你可能手动创建的任何数据库连接所需的代码,释放可能保留的资源或防止对象被破坏,或者只是清理大型资源池(例如图像) )。 这还包括非托管资源,例如,标记在“不安全”块内的代码本质上是非托管代码,可以允许直接内存操作,这肯定需要清理。

    If the class in question is Microsoft-supplied (ie.. database, etc..) then the handling of Dispose (from IDisposable) will most likely already be taken care of, it is just up to you to call it. For instance, standard practice using a database would look like:

    //...
    using (IDataReader dataRead = new DataReaderObject())
    {
       //Call database
    }
    

    This is essentially the same as writing:

    IDataReader dataRead = null;
    try
    {
        dataRead = new DataReaderObject()
        //Call database
    }
    finally
    {
        if(dataRead != null)
        {
            dataRead.Dispose();
        }
    }
    

    From what I understand, it is generally good practice for you use the former on objects that inherit from IDisposable since it will ensure proper freeing of resources.

    As for using IDisposable yourself, the implementation is up to you. Once you inherit from it you should ensure the method contains the code needed to dispose of any DB connections you may have manually created, freeing of resources that may remain or prevent the object from being destroyed, or just cleaning up large resource pools (like images). This also includes unmanaged resources, for instance, code marked inside an "unsafe" block is essentially unmanaged code that can allow for direct memory manipulation, something that definitely needs cleaning up.

    贱贱哒 2024-08-01 11:36:29

    “非托管资源”一词有些用词不当。 基本概念是配对操作——执行一个操作会产生执行某些清理操作的需要。 打开文件会产生关闭它的需要。 拨打调制解调器需要挂断电话。 系统可能会在执行清理操作失败时幸存下来,但后果可能很严重。

    当一个对象被称为“持有非托管资源”时,真正的意思是该对象具有对某些其他实体执行某些所需的清理操作所需的信息和动力,并且没有特殊的理由相信信息和动力存在于其他任何地方。 如果唯一具有此类信息和动力的对象被完全废弃,则所需的清理操作将永远不会发生。 .Dispose 的目的是强制对象执行任何所需的清理,以便可以安全地放弃它。

    为了防止代码在没有首先调用 Dispose 的情况下放弃对象,系统允许类注册“终结”。 如果注册类的对象被废弃,系统将在该对象被永久废弃之前给该对象一个对其他实体执行清理操作的机会。 然而,无法保证系统会多快注意到对象已被放弃,并且各种情况可能会阻止对象获得清理的机会。 术语“托管资源”有时用于指在放弃之前必须执行一些清理的对象,但如果有人无法调用 Dispose,它将自动注册并尝试执行此类清理。

    The term "unmanaged resource" is something of a misnomer. The essential concept is that of a paired action--performing one action creates a need to perform some cleanup action. Opening a file creates a need to close it. Dialing a modem creates a need to hang up. A system may survive a failure to perform a cleanup operation, but the consequences may be severe.

    When an object is said to "hold unmanaged resources", what is really meant is that the object has the information and impetus necessary to perform some required cleanup operation on some other entity, and there's no particular reason to believe that information and impetus exists anywhere else. If the only object with such information and impetus is completely abandoned, the required cleanup operation will never occur. The purpose of .Dispose is to force an object to perform any required cleanup, so that it may be safely abandoned.

    To provide some protection against code which abandons objects without first calling Dispose, the system allows classes to register for "finalization". If an object of a registered class is abandoned, the system will give the object a chance to perform cleanup operation on other entities before it is abandoned for good. There's no guarantee as to how quickly the system will notice that an object has been abandoned, however, and various circumstances may prevent an object from being offered its chance to clean up. The term "managed resource" is sometimes used to refer to an object which will have to perform some cleanup before it's abandoned, but which will automatically register and attempt to perform such cleanup if someone fails to call Dispose.

    没企图 2024-08-01 11:36:29

    为什么它对你来说很重要?

    如果可能的话,我将一次性对象的范围包装在 using 中。 这会在使用结束时调用 dispose。 如果不是,每当我不再需要该对象时,我都会显式调用 dispose。

    无论是原因1还是原因2都没有必要。

    Why should it matter to you?

    When possible I wrap the scope of a disposable object in a using. This calls dispose at the end of the using. When not I explicitly call dispose whenever I no longer need the object.

    Whether for reason 1 or reason 2 is not necessary.

    我的奇迹 2024-08-01 11:36:29

    是的,您有责任调用 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 - 哇,正确执行起来很复杂:)

    编辑2: 我并不是说您应该使用第二种变体。 我只是想表明“使用”对于一堆可能变得非常混乱且难以正确处理的代码来说是很好的语法糖。

    Yes, you're responsible to call the Dispose method - or better use using statement. If an object is implementing IDisposable, you should always dispose it, no matter what.

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

    is similar to

    {
      var myObj;
      try
      {
         myObj = new Whatever();
         // ..
      } 
      finally
      {
        if (myObj != null)
        {
          ((IDisposable)myObj).Dispose();
        }
      }
    } // object scope ends here
    

    EDIT: Added try/finally thanks to Talljoe - wow, that's complicated to get it right :)

    EDIT2: I am not saying that you should use the second variant. I just wanted to show that "using" is nice syntactic sugar for a bunch of code that can get quite messy and hard to get right.

    何其悲哀 2024-08-01 11:36:29

    这里缺少的一个是终结器 - 我的习惯是,如果我实现 IDisposable,我还会有一个终结器来调用 Dispose() 以防万一我的客户不这样做。 是的,它会增加开销,但如果调用 Dispose() ,则 GC.SuppressFinalize(this) 调用会消除它。

    One piece that is missing here is the finalizer - my habit has been if I implement IDisposable I also have a finalizer to call Dispose() just in case my client doesn't. Yes, it adds overhead but if Dispose() IS called than the GC.SuppressFinalize(this) call eliminates it.

    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文