IDisposable, Finalizers and the definition of an unmanaged resource

佐手、 提交于 2019-12-06 03:07:15

问题


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()?

回答1:


  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).




回答2:


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?




回答3:


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.




回答4:


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.




回答5:


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.




回答6:


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.




回答7:


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.




回答8:


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.



来源:https://stackoverflow.com/questions/1013275/idisposable-finalizers-and-the-definition-of-an-unmanaged-resource

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!