Search code examples
c#memory-managementdisposefinalize

How to Properly Handle Class Variables with Dispose/Finalize Methods


I am at a loss on how to handle a class that contains variables with Dispose/Finalize methods. I wish for this class to contain its own Dispose/Finalize methods that call upon the Dispose for each variable; however, both the C# documentation and all other answers/examples on StackOverflow are causing some confusion.

The primary confusion comes from the lack of clarification between what's a "managed" or "unmanaged" object. For example, the documentation here that explains how to implement Dispose simply uses placeholder comments that simply state "Free any unmanaged objects here." or "Free any other managed objects here."

Is a class variable with Dispose/Finalize fall under the category of managed or unmanaged? (Furthermore, should I be concerned about class variables that do NOT contain any sort of Dispose/Finalize implementations? Considering how there's two types of "management", does that mean those without "Dispose" still need to be disposed somehow?)

I.e., what would be the correct way to handle this class?

class BaseClass : IDisposable {

   MyDisposeableObject disposeMe; // object with Dispose/Finalize
   Dictionary<string,int> anotherObject; // just some arbitrary other object

   bool disposed = false;

   public BaseClass() {
      disposeMe = new MyDisposeableObject();
      anotherObject = new Dictionary<string,int>();
   }

   public void Dispose() { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }

   protected virtual void Dispose(bool disposing) {
      if (disposed)
         return; 

      if (disposing) {
         // Free any other managed objects here.
         // Should I use "disposeMe.Dispose()" here?
      }

      // Free any unmanaged objects here.
      // OR should I use "disposeMe.Dispose()" here?

      // Also should I be doing anything with "anotherObject"?

      disposed = true;
   }

   ~BaseClass() {
      Dispose(false);
   }
}

Solution

  • That was also a confusion for me but when I read more about memory management and GC mechanism in .Net everything became clear.

    You should call disposeMe.Dispose() only if "disposing=true". Because it is a managed class/resource. I assume that it also implements this dispose and destructor pattern correctly.

    Why you should not try to use any managed object outside of the if(disposing) block?

    Because GC may not and does not collect your objects by following a graph from owner to owned. So, when the Dispose method is called by Destructor, disposeMe object may already be collected and unreachable. So you can not/should not dispose it in this area.

    But you can free unmanaged resources, like unmanaged memory spaces you allocate, handles you opened... Since GC does not know anything about them, it can not collect and free them unless you intentionally free them. If you don't, there will be memory and handle leaks, which will eventually crash the application.