0

If Finalizer (destructor) is implemented in a class and GC.SupressFinalizer() is called from the overriden Dispose() method will the Garbage Collector still take care of any managed resources the instances of that class might have? What confuses me is Microsofts documentation on this matter. For example the implemenation of IDisposable pattern in the virtual Dispose method takes care of the managed resources as well as the unmanaged. Why is that being done if the GC is taking care of the managed resources by default? If I define a class like this:

public class Car
{
    string plateNum;
}

and use this type as a filed in a class that also deals with unmanaged resources, according to the Microsofts documentation, the proper way to handle the disposal of the objects would be to call Dispose on the Car as well. For one to do so Car has to implement the IDisposable interface. Car is only dealing with the managed resources, there is no reason for doing so in the Car class, I have no idea what Dispose() would even do there, maybe give null to the plateNum? Also why would anyone want to implement IDisposable on the class that deals with the managed resources only? Having that in mind, why is there a section in the code in the virtual Dispose() method (in the example in the MS documentation) in which managed resources are disposed?

protected virtual void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if(!this.disposed)
            {
                // If disposing equals true, dispose all managed
                // and unmanaged resources.
                if(disposing)
                {
                    // Dispose managed resources.
                    component.Dispose();
                }

                // Call the appropriate methods to clean up
                // unmanaged resources here.
                // If disposing is false,
                // only the following code is executed.
                CloseHandle(handle);
                handle = IntPtr.Zero;

                // Note disposing has been done.
                disposed = true;
            }
        }

The only reason that I can think of is that GC.SupressFinalize(Object) is telling to the GC that it doesn't need to take care of anything related to the object argument that is given. But this shouldn't be the case because implementing the finalizer should only mean that the object that implements it should be put on the Finalizer queue only after the object has been dealt with by the GC because the Finalizer method, that is explicitly implemented by the user, should be called. Also if a finalizer is defined does that change the way the GC collects the managed resources that the instance contains or does it just mean that additional code contained in the finalizer will be executed?

  • 1
    SupressFinalize() is an important optimization. The ~Finalizer itself is expensive, and no longer needed after a Dispose(). – H H May 21 '22 at 12:32
  • Although the question is different, you will find the answer [here](https://stackoverflow.com/a/3038602). – NightOwl888 May 21 '22 at 12:35
  • @HenkHolterman I might have a bad understanding of what a managed resource is. Is FileStream a managed resource or unmanaged? Is it a SafeHandle that is wrapped around the unmanaged resource? In that case, again, is it managed or unmanaged resource :D Many things related to these subjects are in my experience explained ambiguously. – Milos Potic May 21 '22 at 12:42
  • @NightOwl888 Not really, doesn't explain why you would call Dispose on any managed resource. But in my reply to Henk I pose a question to what a managed/unmanaged resources clear definition actually is. In various places people use those terms way too loosely so the real meaning is not clear to me at all. – Milos Potic May 21 '22 at 12:45
  • 1
    @MilosPotic - In that case, you asked the wrong question. SO is a question and answer site, not a forum, generally you should ask 1 question per post. An unmanaged resource specifically refers to calling *directly* into native (C/C++) code. These resources require explicit cleanup. So, it is common practice to use a C# finalizer to ensure they are cleaned up whether the client calls `Dispose()` or not. The call to `GC.SupressFinalize()` is to ensure `Dispose()` is not done twice when the client is not missing the `Dispose()` call. – NightOwl888 May 21 '22 at 12:53
  • See [What exactly are unmanaged resources?](https://stackoverflow.com/q/3433197) for more details. – NightOwl888 May 21 '22 at 12:58
  • @NightOwl888 - personally I find the top answer there a very bad answer. Managed resources are classes that implement IDisposable. – H H May 21 '22 at 13:01
  • @HenkHolterman - Although I concede my definition about *unmanaged* resources was incomplete (for example, an open database connection may need to be explicitly closed to prevent resource leaks), your definition of *managed* resources is lacking also. All .NET types are managed resources whether then implement IDisposable or not. Basically, anything that the GC is aware of. Unmanaged resources are things that are not managed by the .NET GC automatically [such as file handles, pinned memory, COM objects, database connections, P/Invoke, etc](https://stackoverflow.com/a/3607223). – NightOwl888 May 21 '22 at 13:09
  • No, in all the .net literature there is an implicit distinction between memory and resources. Memory is food for the GC, resources need to be Disposed. Look at that `component` from the question, you can see it's IDisposable. – H H May 21 '22 at 13:14

1 Answers1

0

There are many Q+A about this already on SO so I'll give you a very practical answer: You won't ever need to write a finalizer.

Summary: In the rare case that you have an unmanaged resource, use the SafeHandle class to make it a managed resource.

When you inspect the full pattern carefully you can see that without unmanaged resources the destructor (aka the Finalizer, ~MyClass() {}) code path does exactly nothing.

And actually having that Finalizer without SuppressFinalize() is very expensive, it will delay the cleanup of your object to the next GC. Promoting its data to gen 1.

The remaining reason for the full pattern with virtual void Dispose(bool) is inheritance. A resource holding class should almost never need that. So make it sealed and all you need (want) is:

public sealed MyClass : IDisposable
{
   private SomeResource _myResource;

   public void Dispose()
   {
      _myResource?.Dispose();
   }
}

And when you need inheritance then this is the official pattern.

H H
  • 263,252
  • 30
  • 330
  • 514
  • So the SafeHandle makes the resource managed. I think that pretty much clears the fog for me in that regard, but I still want to know whether GC.SuppressFinalize() alters the collection process, or all it does is just makes sure that the Finalizer (destructor) will be called at some point and the user defined code from the finalizer will be executed. This is not purely practical implementation question, its more of a interest in how does this actually work. – Milos Potic May 21 '22 at 12:58
  • SuppressFinalize() will _prevent_ the GC from calling the Finalizer. A big win. – H H May 21 '22 at 13:05
  • I just noticed I made a mistake on my first comment, I wanted to say "or all it does is just makes sure that the Finalizer (destructor) will **NOT** be called..." – Milos Potic May 21 '22 at 13:51
  • Yes, and then also "... __NOT__ be executed". So we agree on that part. – H H May 21 '22 at 14:12