Search code examples
c#destructordisposeidisposablefinalizer

Are destructor methods used implicitly by the garbage collector, and dispose methods used by developers to explicitly dispose of objects?


I can see there's already a lot of threads regarding dispose vs. destructor methods but I just want to make sure that I'm understanding them correctly before I move on.

Are destructor methods used implicitly by the garbage collector for when objects are no longer referenced (i.e. no longer needed) and dispose methods used by us developers to explicitly dispose of objects that may not be handled by the garbage collector?

Also - I'm reading into all this now and it seems that it's a case of one or the other with these methods. So for example given the following code:

class DansClass : IDisposable
{
    public void Dispose()
    {
        GC.SuppressFinalize(this);
        Console.WriteLine("Disposing...");
    }

    -DansClass()
    {
        Console.WriteLine("Destructing...");
    }
}

The output will be:

Destructing...

Understandable, as we've suppressed the finalize (destructor) so we only see the Dispose output.

But if I comment out the SuppressFinalize() method the output is:

Disposing...

Why isn't the destructor called as well?


Solution

  • Dispose methods, finalizers, and C#'s ironically-named destructors, exist because many objects ask other entities to do things on their behalf until further notice (typically granting exclusive use of something like a file, a region of memory, communications stream, hardware device, GDI handle, etc.); if an object issuing such a request were to disappear without those other entities know their services are no longer required, anything that had been set aside on behalf of the abandoned object would remain uselessly inaccessible.

    The IDisposable.Dispose method provides a nice consistent way of telling an object that it will no longer be asked to do anything would require the assistance of other entities, and that any other entities which were doing anything on its behalf should be told to stop doing so. If IDisposable.Dispose is called properly, objects can minimize the extent to which outside entities have to act on their behalf.

    Unfortunately, for various reasons (most being easily avoidable; some not), objects are sometimes abandoned without IDisposable.Dispose having been called. This will result in outside entities having to uselessly keep acting, at least for awhile, on behalf of the objects which are abandoned. To avoid having outside entities act forever on behalf of outside entities, the system can notify objects that they have been abandoned, thus giving them a chance to inform outside entities of this fact. Whenever an object whose class overrides Object.Finalize is created, it will be placed in a special list of objects that want to be notified if they are abandoned. Existence on this list, by itself, is not enough to make an object be considered "live", but before the garbage collector removes dead objects from memory it will check whether they are on the notify-before-destruction list. All dead objects which are on the list will be moved from the list of objects requesting notification if/when they are abandoned to a list of objects needing to be notified that they have been abandoned. Placement on this second list will cause the dead objects, and any objects to which they hold references, to be considered "alive" again, at least until the notification has been performed. When they are moved to the second list, however, they are removed from the first list so that if they are later found to be dead again, they will be swept from memory without further notice.

    After the garbage collection completes, if any objects are in the list of objects needing to be notified of abandonment, the system will call the Object.Finalize method of each such object. Typically, once Object.Finalize has been called on such an object, there won't be any more rooted references to it and it will vanish on the next garbage collection. It is possible, however, for objects to be resurrected.

    In vb.net and, so far as I know, most .net languages, one overrides Object.Finalize by simply declaring an override in the usual fashion. For whatever reason, the creators of C# decided to forbid that. Instead, in C#, one must use a language structure, ironically called a "Destructor", to override Finalize so that objects which are found to be abandoned will not be destroyed without notice, but instead be given a chance to clean up.

    There are many tricky wrinkles in the actual operation of Object.Finalize, and it is best to avoid relying upon it except when absolutely necessary. The ironically-named "destructors" don't actually destroy objects, but rather delay their destruction. Calling GC.SuppressFinalize() on an object will remove it from the list of objects requesting notification when they are abandoned; provided that Dispose is called on an object, and it in turn calls GC.SuppressFinalize() as its last operation, there isn't any particular harm in having an object override Finalize or declare a "destructor", but in general it's best to only override Finalize (or declare "destructors") in relatively simple classes.