I saw this answer from Brian Grunkemeyer, a dev on the CLR team, recently… I thought it would generally useful… let me know what you think?
A: The reason we throw an exception is pretty simple – WeakReferences are finalizable objects, just as your objects (TextChunk) is finalizable. The CLR provides no ordering among finalizers. This is a big departure from C++’s destructors, and basically means that if you write a finalizer, you should not count on being able to use other finalizable objects at all. The GC is free to run your WeakReference finalizer before your TextChunk finalizer – that change is within the established contract for finalization, and is not a bug in the CLR. Your code must deal with this. (There are two other cases that could cause this – your constructor could throw an exception, letting you finalize a partially constructed object, or you ran into some interesting behavior on appdomain unloads or process shutdown. Yet all these cases are your problem.)
You have two options here:
1) Write your finalizer to deal with a WeakReference that may have been finalized. You must both check to see if the WeakReference’s Target property returns null AND you must also put a try/catch around your code using the Target property, to catch an InvalidOperationException (please catch just this exception – catch(Exception) is evil for other reasons). Then if you don’t get back a valid object from your WeakReference, write your code to handle this case. In the future, you may need to do some synchronization here as we may add in multiple finalizer threads, so if the target of the weak reference is also finalizable, you may have some very tricky synchronization issues when running on a future version of the runtime.
2) Use our IDisposable pattern, where your Finalize and Dispose() methods call a private or protected Dispose(bool disposing), setting the Boolean to true from the Dispose() method and false from the Finalize method. From Dispose(bool), you can safely touch other finalizable objects if and only if the disposing parameter was true. Then you simply write your code to not touch your WeakReference at all if called from your finalizer.
#2 is vastly simpler, and is the design pattern we’ve gone with for the vast majority of Framework types. I don’t know of anyone who is trying to make #1 work and has succeeded in V1\V1.1. And I don’t know if their code will be correct in the presence of multiple finalizer threads.
If you need a better understanding of this, you might consider reading a section of Jeffrey Richter’s Applied Microsoft .NET Framework Programming. Chapter 19 discusses the GC, and in particular pages 471 through 489 cover the Dispose pattern, C#’s using statement, and WeakReference. The OSHandle class he proposes in there is a primitive version of the SafeHandle class we are working on for Whidbey. Additionally, after you understand all of this you might want to look at Environment.HasShutdownStarted, so you can tell whether any of the objects held in static variables may have been finalized, which happens somewhat late during process shutdown and appdomain unloads.