[Mono-list] Accessing member objects in destructors

Jonathan Pryor jonpryor@vt.edu
Thu, 04 Mar 2004 17:28:12 -0500


Below...

On Thu, 2004-03-04 at 15:01, Uppenborn, Jason wrote:
<snip/>
> For a *class* destructor, I would say that accessing methods of member
> objects would be correct because, AFAIK, as there isn't any other
> point at which the class is told to release it's resources.

What, pray tell, is a class destructor, as opposed to an object
destructor?  The destructor syntax is used to implement the class
finalizer, which is very different from a C++ destructor.  The finalizer
overrides System.Object.Finalize, and is thus a method, and only applies
to instances of a class, not the class itself.  I'm not sure what a
class destructor would be.  I can imagine it would be something called
when the class is unloaded, in which case listening to the
AppDomain.Unload event is the nearest equivalent...

> For an *object* destructor, I would say that accessing methods of
> member objects is incorrect: implement IDisposable instead. As I
> understand it, disposal happens as requested or when an object is made
> available for garbage collection, whereas destruction is done whenever
> the garbage collector decides to get around to it. When destruction
> happens, you have no guarantee that any of your references to member
> objects are still valid.
> 
> I'm sure someone will correct me if I'm wrong, but for now that's my
> two cents.

Here's the correction: System.Object.Finalize is called when the object
is collected by the Garbage Collector.  You should NEVER access member
objects from your finalizer, as those members may have already had their
Finalize methods called, though memory may not have been reclaimed yet.

.NET's GC does NO ordering of object finalization, so it's quite
possible for your members to be finalized before you are.

Exception: Unmanaged resources.  The GC doesn't know anything about
them, so it's your responsibility to deal with these resources.

Exception 2: You really know what you're doing, or you're part of the
runtime infrastructure itself, and make sure that all objects involved
can have their Finalize (or Dispose) methods called *safely* multiple
times.  One example I heard about was .NET's System.Threading.Thread
finalizer, which needs to make sure it doesn't finalize the Finalizer
thread. :-)

This is why the typical IDisposable implementation only accesses members
when Dispose was explicitly called.  When the finalizer executes, nested
members aren't accessed.  For example:

	class Test : IDisposable {
		IDisposable nested;

		public void Dispose ()
		{
			Dispose (true);
		}

		~Test ()
		{
			Dispose (false);
		}

		protected virtual void Dispose (bool disposing)
		{
			if (disposing) {
				// safe to access members
				if (nested != null)
					nested.Dispose ();
			}
			// Always deal with unmanaged members here.
		}
	}

 - Jon