9

In other words,

class Foo
{
    object obj;
    Foo() { obj = new object(); }
    ~Foo() { obj.ToString(); /* NullReferenceException? */ }
}
CannibalSmith
  • 4,742
  • 10
  • 44
  • 52
  • While it's not an answer to your question, I write my finalizers and dispose methods like I write C code, checking before I do. `if (obj != null) obj.dispose()` This is just because it's such a pain to track down and deal with exceptions in these methods – C. Ross Feb 24 '10 at 15:59
  • 2
    Do you really need a finalizer? Most C# classes should **not** have one. – Joel Coehoorn Feb 24 '10 at 16:01

5 Answers5

7

From Object.Finalize:

The finalizers of two objects are not guaranteed to run in any specific order, even if one object refers to the other. That is, if Object A has a reference to Object B and both have finalizers, Object B might have already finalized when the finalizer of Object A starts.

In short, you can't make any assumptions about the state of referenced objects during a finalizer.

In virtually all circumstances, the logic implemented in a finalizer belongs in the Disposable pattern. This is an example of how to correctly implement the pattern in .NET using the IDisposable interface.

public class MyClass : IDisposable
{
    private bool _disposed;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if(disposing)
            {
                // Release unmanaged resources.
            }

            // Release managed resources (Streams, SqlConnections, etc.)
        }

        _disposed = true;
    }
}

In the unlikely event you're working with unmanaged resources, have a look at this article for how to implement IDisposable with a finalizer:

MDSN: Implementing Finalize and Dispose to Clean Up Unmanaged Resources

Paul Turner
  • 38,949
  • 15
  • 102
  • 166
  • 3
    Just because you implement `IDisposable` does *not* mean you should implement a finalizer. In fact, you almost never want to implement a finalizer. However, if you do implement a finalizer you should also implement `IDisposable`. – Scott Dorman Feb 24 '10 at 16:26
  • 1
    You only want a finalizer if your object directly contains unmanaged resources. You should also be wrapping your unmanaged resource handles into objects like `SafeFileHandle` so the resulting finalizer does not place undue load on the GC. – Sam Harwell Feb 24 '10 at 16:31
  • I agree that you don't want to implement a finalizer unless you're handling unmanaged resources. I had hoped overloading `Dispose` would make that clear, but I'll re-do my code example to work with just the managed resources scenario. Just for you guys ;) – Paul Turner Feb 24 '10 at 16:51
  • Actually, your original code example was spot on if you just removed the finalizer. To properly implement the Dispose pattern, you do want a `public virtual void Dispose(bool disposing)` method which contains all of your disposal logic (as you had in your original code) and the `IDisposable.Dispose()` method implementation should simply call that one (again, as you had in the original example). You just didn't need to implement the finalizer. – Scott Dorman Feb 24 '10 at 17:35
  • Curiously, why would you want the overloaded Dispose if you aren't handling unmanaged resources? Is it just as an extension point in the unlikely event somebody wants to extend your class with unmanaged resources? – Paul Turner Feb 25 '10 at 13:40
  • @Tragedian: A public class should implement `IDisposable` if it encapsulates and owns resources, *whether managed or unmanaged*. Managed resources have a built-in "safety net" so that they'll often get cleaned up, eventually, even if they're abandoned, but resources of any type should almost always get cleaned up as early as practical (without waiting for the system to notice that they've been abandoned), and `IDisposable` is the proper way to achieve that. – supercat Dec 11 '13 at 17:26
7

It's not safe since obj might have already been garbage collected. Also note that the garbage collector will not set the reference to null. So even checking for obj != null will not help you.

See here for details: http://msdn.microsoft.com/en-us/magazine/cc163392.aspx#S3

"Generalizing this principle, in a Dispose method it’s safe to clean up all resources that an object is holding onto, whether they are managed objects or native resources. However, in a finalizer it is only safe to clean up objects that are not finalizable, and generally the finalizer should only be releasing native resources." (Your obj is finalizable, so you shouldn't touch it in another finalizer)

That's also the reason why you have the

if (disposing) {...}

in the IDisposable pattern (see Figure 2 in the link above).

stmax
  • 6,506
  • 4
  • 28
  • 45
  • 2
    So the answer is: it's safe *if* you know *for sure* that the referenced object is also referenced by other live objects. – CannibalSmith Feb 25 '10 at 11:20
3

When an object registers for finalization, it is placed on the finalization queue. When the garbage collector runs, all objects are divided into three categories:

  1. Those which have a rooted live reference other than the finalization queue.
  2. Those for which the only rooted reference is the finalization queue.
  3. Those for which no rooted reference exists.

Objects of the third type will simply cease to exist, though nothing will ever notice. Those of the first type are considered "live". Those of the middle type (including objects with finalizers, and other objects to which finalizable objects hold references), will have their finalizers (if any) run; once the finalizers are completed, unless the finalizers store rooted references someplace or re-register the objects for finalization, the objects will no longer have any rooted references at all and will be eligible for the next garbage collection.

The only real danger using references to other objects during finalization is that unless the objects have suitable interlocks, there's no way of knowing what state they might be in when the finalizer tries to clean them up; it's possible they might even be in use. It is probably a good idea to use Threading.Interlocked.Exchange to test-and-set a flag to indicate cleanup is in progress.

BTW, a couple of additional points Microsoft doesn't emphasize in their documentation:

  1. Because any objects to which finalizable objects hold direct or indirect references are ineligible for garbage collection, objects with finalizers shouldn't hold references to any objects not needed for finalization.
  2. Although Microsoft's Dispose pattern attempts to facilitate cleanup of objects with both managed and unmanaged resources (I think better terms would be self-cleaning and non-self-cleaning), such objects are almost always going to hold resources to objects not needed during finalization (falling afoul of rule #1).

Better, IMHO, would be to adopt the following principle: non-self-cleaning resources should be placed in their own classes, whose sole purpose is to handle their cleanup and expose the objects for use elsewhere; those should be the only classes with finalizers. Only classes derived from Object should implement finalizers; other derived classes which add non-self-cleaning resources should encapsulate those resources into separate self-cleaning classes, and then hold references to them.

supercat
  • 77,689
  • 9
  • 166
  • 211
1

In general, you don't want to implement finalizers on your objects. If you need to perform resource cleanup of managed objects, you want to do that in Dispose and properly implement the Dispose pattern.

If you do end up implementing a finalizer, you only want to access unmanaged resources.

Scott Dorman
  • 42,236
  • 12
  • 79
  • 110
0

If this is a concern, then it's probably better to dispose than finalize.

Joel
  • 5,618
  • 1
  • 20
  • 19