Search code examples
c#.netgarbage-collectionfinalizer

When should I NOT call GC.SuppressFinalize(this) in Dispose?


I have a simple class like this that implements IDisposable and so it's hit by a CA1816: Call GC.SuppressFinalize correctly:

public class A : IDisposable
{
    // ...
    int foo

    public A()
    {
        SomeObj.SomeEvent += DoSomething;
    }

    public void Dispose()
    {
        SomeObj.SomeEvent -= DoSomething;
    }
}

Change A.Dispose() to call GC.SuppressFinalize(object). This will prevent derived types that introduce a finalizer from needing to re-implement 'IDisposable' to call it.

Now I have read this, this and this but I still do not understand exactly what that is for (more explanation below). So my questions are:

  • Should I always just add GC.SuppressFinalize(object) into my Dispose method if I do not understand what it is?

  • When should I NOT do this and just suppress the warning?


To add more context: I still do not understand what SuppressFinalize is doing. I know I already disposed some resources in Dispose (event handlers in the example, or IDisposable resources etc) but what about the variables like int foo that still need to be cleaned up? And what about the warning part of "This will prevent derived types that introduce a finalizer from needing to re-implement"?


Solution

  • Finalizer or destructor is a legacy of C++, but in C# it extends the time the GC takes for objects to be collected.

    Unnecessary finalizers, including empty finalizers, finalizers that only call the base class finalizer, or finalizers that only call conditionally emitted methods, cause a needless loss of performance.

    When you use the dispose pattern, you manually release (or by the using statement) resources, finalizers are no longer needed. The method GC.SuppressFinalize is to tell GC to ignore the finalizer of the target and collect it like a regular object, thereby improving the performance of GC. If you don't call this method, you lose the meaning of using the dispose pattern, so it's a good practice to always add it.

    Partly I am confused because I have no such thing in my class and still receive the warning.

    Yes, your class does not have a finalizer, but you cannot guarantee that its subclasses do not either. Note that this analysis will not be reported in sealed classes (according to my testing, internal/private classes will also not).

    When should I NOT do this and just suppress the warning?

    It should be never. As mentioned above, even if you don't call GC.SuppressFinalize, it usually only affects performance. Unless you release resources in the dispose method and in the finalizer separately, but it feels like this is some kind of programming mistake.