0

Can't seem to find consistent answers on the Internet. It looks like CLR GC makes a "decision" on whether to call destructors during garbage collection. This would imply that sometimes destructors won't be called. The way our legacy code base is structured, "using" blocks can't always be used, and the object instances do have certain resources that need to be released after object is no longer needed. I need a guaranteed way for GC to call a destructor. Would implementing IDisposable guarantee .Dispose being called on garbage collection? What is the best practice here?

I tried using ~ClassName(){ } and destructor doesn't always get called.

  • If you know what you are doing, you could derive from [CriticalFinalizerObject](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.constrainedexecution.criticalfinalizerobject) for guranteed finalization. – Klaus Gütter Jan 16 '23 at 19:49
  • You believe a lot of myths about the garbage collector. That's OK. Lots of people do. But you will not be successful until you replace those false beliefs with true ones. Please do read my articles linked in the answer below. – Eric Lippert Jan 17 '23 at 22:15
  • The GC does not "decide to call destructors"; the GC doesn't call destructors at all! The finalizer thread calls destructors. The GC thread *does* decide whether an object goes on the finalizer queue or not. – Eric Lippert Jan 17 '23 at 22:18
  • And no, there is no connection *whatsoever* between disposables, the GC, and destructors, EXCEPT that it is *best practice* to (1) write a destructor that detects when an undisposed object is finalized, and make sure it does the right thing, and (2) write a disposer that marks the object as not finalizable, so that you don't extend the lifetime of a disposed object in order to do a redundant check. – Eric Lippert Jan 17 '23 at 22:28
  • If you can say more about *why* you need a *guarantee* that a destructor is called -- , there is no such guarantee, so your requirement is going to go unmet -- then maybe someone can help you solve the actual problem you have. Try to avoid "XY questions". An XY question is one where you ask how to achieve a wrong solution, rather than asking how to solve the real problem. – Eric Lippert Jan 17 '23 at 22:36
  • There *are* ways to use constrained execution regions to achieve stronger guarantees, but that's an advanced technique that might not be suited to your legacy application. We'll need more information to advise you further. – Eric Lippert Jan 17 '23 at 22:56

2 Answers2

1

The finalizer (destructor) is unreliable.

Ideally you should implement the IDisposable interface and use using blocks whenever possible. This article from microsoft has info about this

For your case a combination of IDisposable and finalizers may be the best case. There is a pattern from microsoft that you can also generate automatically in Visual Studio.

From microsoft:

using System;

class BaseClassWithFinalizer : IDisposable
{
    // To detect redundant calls
    private bool _disposedValue;

    ~BaseClassWithFinalizer() => Dispose(false);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects)
            }

            // TODO: free unmanaged resources (unmanaged objects) and override finalizer
            // TODO: set large fields to null
            _disposedValue = true;
        }
    }
}
Nick Vidalis
  • 111
  • 2
0

Destructor

The preferable term is finalizer AFAIK.

It looks like CLR GC makes a "decision" on whether to call destructors during garbage collection

AFAIK GC does not call finalizers during collection, they are called after GC marks objects ready for finalization (this also results in extending object lifespan which can have performance implications) and places them into finalization queue. In "normal" program flow finalizers should eventually be run but there are some corner cases which can prevent that.

I need a guaranteed way for GC to call a destructor.

There is no such way. Finalizers are non-deterministic and should be used as "last resort" measure.

Would implementing IDisposable guarantee .Dispose being called on garbage collection?

No, IDisposable has nothing to do with GC, IDisposable will be called only "manually" (i.e. either directly or via using), that's it.

What is the best practice here?

Implement IDisposable and invoke it (manually or via using).

Useful resources:

Guru Stron
  • 102,774
  • 10
  • 95
  • 132