IDisposable GC.SuppressFinalize(this) location
Asked Answered
M

5

11

I use a default IDisposable implementation template (pattern) for my code.

snippet:

public void Dispose()
{
    Dispose(true);

    GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool isDisposing)
{
    if (!this.disposed)
    {
        if (isDisposing)
        {
            //cleanup managed resources
        }

        //cleanup unmanaged resources

        this.disposed = true;
    }
}

My question: why is the call "GC.SuppressFinalize(this)" in the Dispose public method? I would place "GC.SuppressFinalize(this)" in the "if (isDisposing)" section of the protected method, after disposing managed resources.

Like this:

protected virtual void Dispose(bool isDisposing)
{
    if (!this.disposed)
    {
       if (isDisposing)
       {
           //cleanup managed resources

           GC.SuppressFinalize(this);
       }

       //cleanup unmanaged resources

       this.disposed = true;
    }
}
Mojave answered 3/3, 2009 at 7:49 Comment(2)
As Stackoverflow supports tagging, you should not prefix your message title with [.NET] ;-)Unfriended
Similar Question - #2605912Cargill
R
12

I suppose its a clear case of Template Design pattern.

Your abstract class is Designed to take care of all important/necessary tasks required (Here, GC.SuppressFinalize(this)), and allowing a derived class to override only some part of the code.

There are 2 cases here:
Snippet 1, SuppressFinalize, in Dispose
Snippet 2, SuppressFinalize, in Dispose(true)

Here, Snippet 1, makes sure that GC.SuppressFinalize is always executed. While snippet 2, leaves the execution of GC.SuppressFinalize at the mercy of derived class.

So, by putting GC.SuppressFinalize, in Dispose method, you as a designer of your class will always make sure that irrespective of whatever code written by derived classes, GC.SuppressFinalize will be executed.

This is only the benefit of writing SuppressFinalize in Dispose rather then Dispose(true).

Rahman answered 3/3, 2009 at 8:55 Comment(3)
When the derived class omits the base dispose call then more resources are not cleaned up...Mojave
I think it is the responsibility of the derived class to implement the base calls correctly, so I still prefer my solution.Mojave
@PatrickPeters: If a derived class has resources which need to be cleaned up after those of the parent, GC.SuppressFinalize should not be called until that derived class has finished its cleanup. Putting the SuppressFinalize call within the virtual method would result in its being called early.Bluepencil
S
6

The Dispose(bool isDisposing) method isn't part of the IDisposable interface.

You would normally call Dispose(true) from your Dispose method, and call Dispose(false) from your finalizer, as a fallback in the case where the object hasn't already been disposed.

Calling SuppressFinalize tells the GC that there's no need to call your object's finalizer, presumably because all your cleanup was done when Dispose was called.

If you don't have a finalizer on your class, then you don't need to call SuppressFinalize at all, since there's no finalizer to suppress!

Joe Duffy has some great guidelines on disposal, finalization, garbage collection etc.

Sumba answered 3/3, 2009 at 10:56 Comment(5)
Thanks for the info, but is this on-topic ? Look at my specific question, it's about the location of the suppress call.Mojave
The examples in your question don't have finalizers, and if that's also the case in your "real" code then calling SuppressFinalize is irrelevant/unnecessary wherever you do it, because there's no finalizer to suppress.Sumba
@Henk, Absolutely right, I should've made that clear in my answer. The point I'm trying to make is that where you call SuppressFinalize is completely irrelevant if your class doesn't have a finalizer in the first place!Sumba
@Henk, Not necessarily. Finalizers are expensive. You should only implement a finalizer if you absolutely need to, and you shouldn't need to unless your class handles unmanaged resources directly.Sumba
The example I used here is with unmanaged resources in mind. When using unmanaged resources you must use a finalizer to cleanup the unmanaged stuff when the Dispose() call hasn't been used.Mojave
B
1

I think either layout could have been chosen, but probably they wanted to emphasize "put all deallocation code in this method" in the protected Dispose method, so they put the other artifact of disposing (Suppressing finalization) elsewhere.

Also, suppose a derived class had another reason for calling the protected Dispose method, but did still want finalization to occur (for whatever imagined reason, I don't know).

Bidding answered 3/3, 2009 at 8:4 Comment(4)
But what if I call the public Dispose of several times (because that should be possible according the guidelines), the suppress call is also called several times. That shouldn't be the case... In my code example it will be called just once.Mojave
Although Dispose can be called several times, it's generally a symptom of problems elsewhere in the codebase - two or more pieces of code beliefing that they control the life of the object. Similarly, GC.SuppressFinalize can be called multiple times.Bidding
Though it could be flaw in code when multiple calls of Dispose is called, it should be programmed defensively so that it can handle it.Mojave
Calling SuppressFinalize multiple times is not a problem. It is a sign of problems elswhere though.Pforzheim
R
1

Cause .Dispose is when managed code (your code) disposes of the object thereby opting out of finalisation. People usually create another route into Dispose(bool disposing) via a finaliser and the call wouldn't make any sense for the finaliser to make.

Rhyme answered 3/3, 2009 at 10:20 Comment(0)
K
0

The idea is that your cleanup code should only be called once. However there are two points of entry: the Dispose method and object finalizers. When Dispose is called, you opt-out of finalization so that your cleanup code is only called once. The code here might illustrate it better.

Quote:

// NOTE: Leave out the finalizer altogether if this class doesn't 
// own unmanaged resources itself, but leave the other methods
// exactly as they are. 
Kilocalorie answered 3/3, 2009 at 7:54 Comment(2)
My example is an complex base class structure with IDisposable. I have searched many scenario's with IDisposable implementations (templates), but didn't find a correct base-class -> derived class example.Mojave
it is not a complete example @ MSDN: MSDN shows only the base-class implementation and not a base-class/derived class example.Mojave

© 2022 - 2024 — McMap. All rights reserved.