9

I have seen the code below a lot of times in different threads and different forums. This one in particular I picked up from How does GC and IDispose work in C#?.

class MyClass : IDisposable
{
    ...

    ~MyClass()
    { 
        this.Dispose(false);
    }

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

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        { /* dispose managed stuff also */ }

        /* but dispose unmanaged stuff always */
    }
}

My questions are:

  1. Is it necessary to create an explicit destructor? The class inherits from IDisposable and during GC cleanup Dispose() will be eventually executed.

  2. What is the significance of the parameter 'disposing' in Dispose(bool disposing)? Why is it necessary to differentiate between disposing of managed and unmanaged objects?

Community
  • 1
  • 1
Ronald
  • 1,532
  • 4
  • 18
  • 34
  • 1
    The canonical resource on IDisposable is Joe Duffy's here: http://www.bluebytesoftware.com/blog/2005/04/08/DGUpdateDisposeFinalizationAndResourceManagement.aspx. – Jeremy McGee Aug 25 '11 at 08:40
  • Does the answer in this question help at all: http://stackoverflow.com/questions/898828/c-finalize-dispose-pattern ? – Adam Houldsworth Aug 25 '11 at 08:40
  • A lot of great answers and links. Really wish I could pick all as the best answer. Thanks guys for sharing! – Ronald Aug 25 '11 at 09:22

5 Answers5

5

The garbage collector itself knows nothing about IDisposable. It will not dispose anything for you - all it will do is call the finalizer.

The point of having the overload with a "disposing" parameter is that the finalizer will call Dispose(false) to indicate that it's been called from the finalizer and managed objects don't need any clean-up, whereas if you call Dispose explicitly (e.g. via a using statement) that will end up calling Dispose(true).

Part of the point of this pattern is that it's extensible for derived classes - only the base class finalizer needs to call Dispose, and everything else will piggy-back on that by overriding Dispose(bool) if necessary.

However, unless you've actually got direct access to unmanaged resources - or expect derived classes to - you probably don't need a finalizer at all. If you do need fairly direct access, then SafeHandle helps to avoid the need to write a finalizer. You should almost never need to write a finalizer these days. Personally I rarely implement IDisposable myself, and when I do it's typically from a sealed class (as I like to seal classes where possible) - and it almost never involves a finalizer... so I just write a single Dispose method to implement the interface and leave it at that. Much simpler.

The full advice for implementing IDisposable in every situation you can imagine is extremely long-winded and complicated. I think it's well worth trying to limit yourself to simpler situations wherever possible.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • `IDisposable` is great for nulling events. Even if I don't have a finalizer I still create the `Dispose(bool)` so that inheritors can call it correctly (which I guess is moot in the case of sealed classes). Also your link isn't thread safe, `_isDisposed` should be an `int` and should be checked/updated via `if (Interlocked.Exchange(ref _isDisposed, 1) == 0) { /* Object was not disposed before method was invoked */ }` – Jonathan Dickinson Aug 25 '11 at 08:54
  • @Jonathan: I haven't read it all the way through recently, but I suspect it talks about the threading somewhere. It's written by people who know about that sort of thing - and it does explicitly mention in at least one example: "Note: The following code does not handle thread-safety issues, it is meant to convey the concept only." – Jon Skeet Aug 25 '11 at 08:59
  • Ah, I merely skimmed over it. Quite a useful resource non-the-less (as a side-note on finalized IDisposables threading is ALWAYS an issue, even if your code isn't multithreaded). – Jonathan Dickinson Aug 25 '11 at 09:28
3

1 Is it necessary to create an explicit destructor?

Only in the rare case that you are directly owning an unmanaged resource.

The class inherits from IDisposable and during GC cleanup Dispose() will be eventually executed.

The IDisposable interface only enables the use in using(){}blocks. The GC will eventually call Dispose() but that will be (too) late. Note that it will use disposing==false and only try to clean up unmanaged stuff. Which you most likely don't have.

2 What is the significance of the parameter 'disposing' in Dispose(bool disposing)? Why is it necessary to differentiate between disposing of managed and unmanaged objects?

Because there is no need to Dispose() the managed resources when you are Disposing. The algorithm of the GC ensures that your managed resources are already being GC'ed themselves. Calling their Dispose() is at best harmless.

Note that this code is based on the standard implementation pattern. If you leave out the destructor the only reason for the overloaded Dispose(bool) is possible inheritance.

A shorter version, note the sealed :

sealed class MyClass : IDisposable
{
   private FileStream MyManagedResource;

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

       /* dispose managed stuff  */
       if (MyManagedResource != null)
          MyManagedResource.Dispose();  // this is why we do it all
   }

   // ~MyClass() { }

}
H H
  • 263,252
  • 30
  • 330
  • 514
2

1: no; that is common only in classes that directly wrap an external unmanaged resource, such as windows-handles; or if it is possible that some subclass will do such. If you are only handling managed objects, adding a finalizer is actually a bad thing, in that it impacts the wat collection works

2: it tells the Dispose(bool) code whether it is currenty in garbage collection (if false). When being collected, you shouldn't touch any other objects outside your own, as they may already be gone. However, if you are being electively disposed (i.e. true) you might want to clean up a few encapsulated managed objects; call .Close() on a connection you are wrapping, for example

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
1
  1. No you do not need to implement a destructor. Actually this is strongly NON recommended by Microsoft itself.
  2. disposing used to identify exact caller of Dispose method.
Tigran
  • 61,654
  • 8
  • 86
  • 123
  • `s/destructor/finalizer/`. The latter is connected to deterministic cleanup, which is precisely what you DON'T get. –  Aug 25 '11 at 08:45
  • @delnan: The C# specification from Microsoft calls it a destructor. The ECMA spec calls it a finalizer. It's an unfortunate naming choice, but it's still correct to call it a destructor. – Jon Skeet Aug 25 '11 at 08:46
  • @delna But destructor is the _official_ term in C#. It has been noted before that that might have been a bad choice. – H H Aug 25 '11 at 08:48
  • @Jon Skeet: Yes, I realize some official material calls it a destructor. But as you say, it's an unfortunate name, so I strongly recommend stop using it. –  Aug 25 '11 at 08:48
  • @delnan to be more clear then ECMA specs :) I mean the method with ~ is destructor – Tigran Aug 25 '11 at 08:48
  • @delnan: I think the C# specification is more than just "some official material" - and I agree it's clearer to use finalizer, but I think your original comment effectively suggested that Tigran was *incorrect* to use destructor, which isn't the case IMO. – Jon Skeet Aug 25 '11 at 08:49
  • @Jon Skeet: Yes, it's not "wrong", and perhaps my original comment should have been clearer on that point (sadly, it's too late to edit now). –  Aug 25 '11 at 08:51
  • @Jon Skeet: I mean implementing destructor (~) is something that is NOT reccomended by MS, cause if you want to destroy somethign esplicitly, create a method and call it. Am I wrong ? – Tigran Aug 25 '11 at 08:53
  • @Tigran: It's recommended that you implement a finalizer/destructor if you have direct access to unmanaged resources, but that's extremely rare - even rarer since the introduction of `SafeHandle`. Will find the relevant Joe Duffy link and edit it into my answer... – Jon Skeet Aug 25 '11 at 08:54
0

The destructor is the reason that during GC cleanup Dispose will be executed, as you mention. So you need it to ensure that the object is eventually disposed if the programmer did not explicitly dispose of it earlier.

The very reason for the existence of IDisposable is to return unmanaged resources to the system. This is the reason that you have the "disposed unmanaged stuff always" comment: unmanaged resources should be disposed both when the programmer explicitly calls Dispose and when the finalizer is executed (however, note that the parameterless Dispose method will explicitly prevent the finalizer from being executed -- this prevents double-disposing of resources and is also good for performance).

The disposing parameter serves to differentiate between the explicit Dispose (by the programmer) and the implicit disposal of resources triggered inside the finalizer. If your class has members of type IDisposable then it's more than likely that disposing your object should also dispose of those other member objects, so the code also runs the "dispose managed stuff also" branch. On the other hand, if you did not dispose of the object explicitly (and it's the finalizer, run by the GC, that does this) then these other objects may have already been garbage-collected. You would not want to touch them if this is the case.

Jon
  • 428,835
  • 81
  • 738
  • 806
  • Re terminology: both finalizer and destructor are used; for example, here's the 2010 MSDN reference: http://msdn.microsoft.com/en-us/library/66x5fx1b.aspx – Marc Gravell Aug 25 '11 at 08:45
  • And `IDisposable` is about doing it in a timely manner - emphasis on the *when*; it doesn't really relate to unmanaged directly, since the finalizer/destructor can do that. There are examples of fully-managed IDisposable implementations – Marc Gravell Aug 25 '11 at 08:47
  • @MarcGravell: Indeed. Trying to fit an essay in a couple paragraphs here :) – Jon Aug 25 '11 at 08:48
  • No, in C# it _is_ called a destructor. – H H Aug 25 '11 at 08:49
  • @HenkHolterman: You do have compelling arguments there, edited to reflect it. IMHO this whole finalizer/destructor terminology is too intricately complicated for its own good. – Jon Aug 25 '11 at 08:53
  • @Jon: see http://stackoverflow.com/q/5882729/60761 and note the link to Eric Lipperts blog. – H H Aug 25 '11 at 09:05