Freigeben über


SYSK 212: Does .NET Garbage Collector call Dispose? Dispose – Best Practices.

Since there are still developers out there that are not clear on if/when should you call Dispose, and what your class’s finalizer should look like if it holds on to non-memory resources such as file handles, GDI objects, database connections, etc., I decided to write this post on Dispose best practices.

 

The first question is – does .NET garbage collector call Dispose for me when destroying the object? The answer is – NO! The good news is that all Microsoft framework classes that implement IDisposable pattern also call Dispose from the class destructor, of course, first checking if resources have already been released. You should too! IDisposable pattern was created so that users of a class holding non-memory resources tell that class when it’s time to release those resources instead of waiting for the garbage collector’s algorithm to kick in. This prevents situations of running out of limited resources (e.g. database connections) on system with high available memory.

 

Best practice: always call Dispose on a class that implements it when you are done with it. In C#, you can use the ‘using’ statement to do that for you transparently. And remember to handle exceptions – code in such a way that you call Dispose regardless of whether your method throws or catches an exception.

 

Best practice: When implementing a class that holds on to non-memory resources beyond the method scope, always implement IDisposable interface. IMPORTANT: in you class’s destructor, check whether the Dispose method was called by the user of your class, and, if not, call it yourself. Consider throwing an exception (in debug only builds) in cases when your destructor was reached and the Dispose was not called.

 

Summary: here is the pattern I use:

      // Thanks Niall, Simon, Anders, Denis, russel, hasanib and James for the comments! Right on!

public class X : IDisposable

{

    private bool _disposed = false;

    // Some non-memory resource declared here

    // and initialized somewhere in this class

    public X()

    {

      

    }

    ~X()

    {

#if __DEBUG

        if (false == _disposed)

            throw new ApplicationException("X.Dispose() was not called!");

#endif

        if (false == _disposed)

            Dispose(false);

    }

    #region IDisposable Members

public void Dispose()    

    {

        Dispose(true);       

 

        GC.SuppressFinalize(this);

    }

 

    // If disposing equals false, the method has been called by the
// runtime from inside the finalizer and you should not reference
// other objects. Only unmanaged resources can be disposed.

    private void Dispose(bool disposing)

    {

        if (!_disposed)

        {

            // TODO: your code here to release resources

           _disposed = true;         

        }

    }

 

    #endregion

}

Comments

  • Anonymous
    October 05, 2006
    You should use GC.SuppressFinalize, otherwise you are adding to collection time and GC workload for every type you use this pattern on, due to adding the finalizer.

  • Anonymous
    October 05, 2006
    The comment has been removed

  • Anonymous
    October 05, 2006
    PingBack from http://www.frogameleon.com/blog/?p=30

  • Anonymous
    October 05, 2006
    I do something like this.public class DisposableClass : IDisposable, DisposableBaseClass{ ~DisposableClass() { Dispose(false); } private bool disposed = false; protected virtual /or override/ void Dispose(bool disposing) { try { if (!this.disposed) { if (disposing) { // Clean up managed resources. } // Clean up unmanaged resources. } this.disposed = true; } finally { base.dispose(disposing); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }}

  • Anonymous
    October 05, 2006
    Better to do this:#if __DEBUG ~X() { if (false == _disposed) throw new ApplicationException("X.Dispose() was not called!"); }#endif

  • Anonymous
    October 05, 2006
    Implenting a destructor in C# just put the class in Finalize queue, it is the GC call Dispose() if the user does not call the dispose. we just do not know we it will be called which results long holding of precious resource.

  • Anonymous
    October 05, 2006
    Great post!!!Why don't you use the "I have a protected virtual Dispose method that takes a boolean indicating whether I'm being called from within the finalizer or user code, and I'll have a public non-virtual method that calls Dispose(true) followed by a call to GC.SuppressFinalize" pattern?

  • Anonymous
    October 05, 2006
    IMHO, this may have been better off as a link to the FDL page @ http://msdn2.microsoft.com/en-us/library/b1yfkh5e.aspxSpecific issues:- Dispose(bool) pattern- suppressing finalization if Dispose() was called- calling out how finalization-time dispose is different (can't cross managed references like you can with a normal dispose)Also, if the non-memory resource happens to exist via a handle, then promoting the use of SafeHandle subclasses (either one of the existing ones or creation of a new one) is a huge help, as it pushes down the problem to an isolated type, helping everything from code complexity to the size of the object graph that is necessarily promoted to Gen 1 due to being in the finalization queue.Anyway, IMHO :)

  • Anonymous
    June 18, 2009
    The comment has been removed