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.
CodePudding user response:
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:
- Already referenced docs
- Possibly a bit dated but wonderful When everything you know is wrong, part one and part 2 by Eric Lippert
- What is the difference between using IDisposable vs a destructor in C#?
GC.WaitForPendingFinalizers
CodePudding user response:
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;
}
}
}