Freigeben über


SYSK 53: To Close() or to Dispose()? That is the question…

Since I’m still meeting developers that are not quite sure of the differences between the two, and the choice that’s right for them, I decided to blog on the topic…

Just so we are all on the same page, you need to call Close or Dispose methods to release non-memory resources, e.g. handles, connections, etc., before the non-deterministic garbage collection does its job.  We all know the golden scalability rule – acquire resources as late as possible and release them as soon as possible…

Of course, if you only have one or the other, it’s an easy decision.  You know you need to call the Close method on Microsoft.Win32.RegistryKey object to release the resources since Dispose is not an option.

But there are a number of classes in .NET, and in some third party products, that have both – Close and Dispose methods.  Examples include System.Data.IDbConnection implementations (e.g. SqlConnection), System.IO.MemoryStream, System.IO.FileStream, System.IO.IsolatedStorage.IsolatedStorageFile, etc.) 

Here is an easy to remember rule I use:  if you plan to reuse the same instance of the object, then call Close(); otherwise call Dispose().   For example, in the following pseudo-code I’d get a run time exception if I were to use Dispose instead of Close:

SqlConnection cn = new SqlConnection(“connection string”);
cn.Open();
// Do some db work
cn.Close();

// Do a lot more non-db work

cn.Open(); // here is where an exception would be thrown if I disposed instead of closing the connection.
. . .

Arguably, this method should be broken into two or three methods with their own instances of SqlConnection class, in which case I’d call Dispose, which will close the connections for me…

Rule of thumb: favor Dispose over Close.   Why?  For three reasons:
1.  Dispose implementation will clean up all its resources…   Who knows, may be there is some class that, underneath the covers, uses several resources that all need to be released.  Close may only release the exposed underlying resource…
2.   Dispose is a .NET implementation of IDisposable interface.  One could say that Close is legacy…
3.  Forces you to be a better (more modular) programmer (see example above).

Comments

  • Anonymous
    February 03, 2006
    Close() and Dispose() do different things and have different contracts.

    Although repeated calls to Dispose() should not have adverse affects you should not assume that a class will be usable again afterwards.

    An example is memory maagement. If you have a hierarchy of classes that are no longer needed then in addition to Dispose() cleaning up any external resources on the top level classes, which may bbe longer lived than the children, it also needs to null out object reference fields to the child classes or they will not be garbage collected.

    The Dispose contract in that case would be likely to throw an ObjectDisposedException.

    A favoured pattern of mine is;
    using(SomeClass class = new SomeClass())
    {
    class.DoStuff();
    } // runtime calls Dispose() here

    The best thing about this is that someone can't then get hold of the instance once it has been disposed.

    This is the closest thing to deterministic finalization you'll get in .NET. Make the most of it :)

  • Anonymous
    February 03, 2006
    Dispose を呼ぶべきか、Close を呼ぶべきか という深遠(?)なテーマ
  • Anonymous
    February 03, 2006
    It's a good topic and has been a big question for me.
    One thing I care when I implement classes is about exceptions.
    According to the design pattern, Dispose(bool disposing) is called from finalizer, where, I believe, exceptions are not recommended. So, when I implement Dispose, I usually try/catch within the implementation and try to do the best effort to recover from the errors.
    Close, on the other hand, is not standardized, so I don't have to worry about implementation of super classes. I don't have to worry about it being called from finalizer, so I feel better to throw and let caller to handle.
    Is this correct understanding? Your opinion would be appreciated.
  • Anonymous
    February 05, 2006
    One thing I usually care when I implement classes is about exceptions.

    Throwing exceptions in Dispose looks like a bad idea to me. Base classes are likely to fail. Dispose(bool disposing) could run in a finalizer thread. C# using statement makes its handling difficult. So I usually try/catch within the Dispose and do the best efforts to recover from the error without any help from users nor callers.

    Close is in different situation. Not all classes have Close, so we don't have to worry much about base clases. I can trust more on callers to handle errors.

    If I'm caller, I'd prefer Close when I want to implement serious error handlings. Otherwise I prefer Dispose, sometimes with try/catch and just Debug.Fail within the catch.

    I couldn't find any guidelines about this up until now. If you have any comments on this, it would be a great help.
  • Anonymous
    February 14, 2006
    You're bringing up a very good point.  I totally agree with you that throwing exceptions from Dispose is not a good idea.  However, in most cases, if you do the job right, there should be no exceptions -- e.g. don't try to close a connection if it's not opened, etc.  I'd argue that a well written object requiring a Close/Dispose method will not need to throw exceptions.  My rule of thumb -- use exceptions for exceptional cases only.  
  • Anonymous
    February 23, 2006
    Thank you for your reply. However, I don't think it's a design issue. Take a FileStream for example. FileStream.Close needs to flush buffer, and disk full could happen. If the file is on a network share, network errors could happen.

    Applications need to know if flush failed, because that means it could not save data properly. Word, for instance, pops up dialog and asks user to save in different location if it could not close the file when saving.
    So, I think excpetions in Close is okay. If you don't allow them, it's hard to write production-level applications.

    If Dispose should not raise exceptions, Close and Dispose should have different implementations. I usually implement "closing operations" in Close. Then in Dispose,
    try { this.Close(); }
    catch { ..do the best recovery }
    But I often see even BCL don't do this.

    It's for sure that there should be a guideline for this from MS.
  • Anonymous
    February 23, 2006
    Very valid points. If I find any formal MS guidance on this, I'll add it to the blog.
  • Anonymous
    June 07, 2006
    According to MSDN http://msdn2.microsoft.com/en-us/library/system.io.stream.close.aspx
    MemoryStream.Close calls Dispose(true) to release both managed and unmanaged resources.  Don't know if this applies to other objects.  Does a using statement block require a Close or Dispose?
  • Anonymous
    June 14, 2006
    I was able to confirm that the System.IO.Stream class does, in fact, call Dispose(true) from Close().  Here are some (e.g. is not intended to be a complete list) classes that do call Dispose(true) from Close:
    RegistryKey, BinaryReader/Writer, StreamReader/Writer, StringReader/Writer, TextReader/Writer, ResourceReader/Writer, WaitHandle,

    However, IsolatedStorage does not call Dispose from Close; instead, it calls Close from Dispose.

    using {} statement block does not require Close or Dispose because the compiler will generate Dispose call for you at the end of the block.