0

While Dispose Pattern is widely discussed on the Internet and SOF I could not find any resource that answer my questions.

Therefore, please be kind to read to the end before marking this as a duplicate. I will happily remove the question if you could point me to a previous question that I have missed.


I have seen many classes that (blindly?) implement dispose pattern without implementing a finalizer.

The MSDN article on Dispose Pattern says:

The Boolean parameter disposing indicates whether the method was invoked from the IDisposable.Dispose implementation or from the finalizer. The Dispose(bool) implementation should check the parameter before accessing other reference objects (e.g., the resource field in the preceding sample). Such objects should only be accessed when the method is called from the IDisposable.Dispose implementation (when the disposing parameter is equal to true). If the method is invoked from the finalizer (disposing is false), other objects should not be accessed. The reason is that objects are finalized in an unpredictable order and so they, or any of their dependencies, might already have been finalized.

According to what I understand GC calls the finalizer method (if implemented) which in turn should call the Dispose(bool) method with parameter as false.

My First question is if a class does not implement a finalizer then does the Dispose(bool) ever get called with parameter as false? (e.g. by something in the CLR that I haven't come across)

I understand Dispose(bool) can be used to make sure that objects are only disposed once.

Therefore, my second question is if a class does not need to implement a finalizer then can it simply implement the Dispose method like below?

private bool objectDisposed;
public void Dispose()
{
    if (!objectDisposed)
    {
        // Release/dispose managed resources
        // ...
        // ...

        objectDisposed = true;
    }
}
CharithJ
  • 46,289
  • 20
  • 116
  • 131
Menol
  • 1,230
  • 21
  • 35
  • [This extensive article on Dispose](https://web.archive.org/web/20051122090032/http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=88e62cdf-5919-4ac7-bc33-20c06ae539ae) will answer all your questions (edit: linking to wayback version as the formatting is crrect there) – stuartd Aug 03 '16 at 10:14
  • [Very relevant to this question...](http://stackoverflow.com/questions/11024336/why-does-sealed-affect-the-implementation-of-idisposable) – spender Aug 03 '16 at 10:14
  • thanks guys I've already read the first one. – Menol Aug 03 '16 at 10:17
  • The link provided by @spender is very relevant because you should only implement this pattern if you are 100% confident that there will **never** be any subclasses of this class - which you can only guarantee by sealing the class. – stuartd Aug 03 '16 at 10:26
  • I wish someone could answer my particular questions :) – Menol Aug 03 '16 at 10:52
  • 1
    The plain answer is "No!" The viral capabilities of the pattern might however act up, if a base class implements and your derived class has something to dispose as well then you can't do anything but override the Dispose(bool) method. – Hans Passant Aug 03 '16 at 11:01
  • @HansPassant thanks, why cant child classes simply call base classes dispose method? similar to calling base's connstructor? – Menol Aug 03 '16 at 11:06
  • 1
    They certainly can. The trouble starts because Dispose() is not usually declared virtual in the Base class. So if client code stores a Derived class object in a Base class reference and calls Dispose() then only the Base class' Dispose() method executes. The disposal pattern requires Dispose(bool) to be virtual. – Hans Passant Aug 03 '16 at 11:24
  • @HansPassant very good point. I didn't consider this aspect. – Menol Aug 03 '16 at 11:27

1 Answers1

2

My First question is if a class does not implement a finalizer then does the Dispose(bool) ever get called with parameter as false? (e.g. by something in the CLR that I haven't come across)

NO

Therefore, my second question is if a class does not need to implement a finalizer then can it simply implement the Dispose method like below?

I wouldn't, but you could. If you do, you will be reinventing the IDisposable pattern, if this class is going to be extended. As HansPassant indicated in the comment, IDisposable patterns comes very handy if there are derived classes. I would rather follow the pattern at the first place.

Base class

using System;

class BaseClass : IDisposable
{
   // Flag: Has Dispose already been called?
   bool disposed = false;

   // Public implementation of Dispose pattern callable by consumers.
   public void Dispose()
   { 
      Dispose(true);
      GC.SuppressFinalize(this);           
   }

   // Protected implementation of Dispose pattern.
   protected virtual void Dispose(bool disposing)
   {
      if (disposed)
         return; 

      if (disposing) {
         // Free any other managed objects here.
         //
      }

      // Free any unmanaged objects here.
      //
      disposed = true;
   }

   // Only if there are unmanaged resources.
   ~BaseClass()
   {
      Dispose(false);
   }
}

Derived Class

using System;

class DerivedClass : BaseClass
{
   // Flag: Has Dispose already been called?
   bool disposed = false;

   // Protected implementation of Dispose pattern.
   protected override void Dispose(bool disposing)
   {
      if (disposed)
         return; 

      if (disposing) {
         // Free any other managed objects here.
         //
      }

      // Free any unmanaged objects here.
      //
      disposed = true;

      // Call the base class implementation.
      base.Dispose(disposing);
   }

   // Only if there are unmanaged resources.
   ~DerivedClass()
   {
      Dispose(false);
   }
}
CharithJ
  • 46,289
  • 20
  • 116
  • 131
  • Either that or make the class sealed so that it cannot be extended. – Lasse V. Karlsen Aug 19 '16 at 06:34
  • We really have to get rid of this disposal pattern nonsense, this answer doesn't help us get there. New Rule: if you don't seal the class then **always** make the Dispose() method virtual. Standard rule in C++ btw. – Hans Passant Aug 19 '16 at 07:17