Jon Davis skrev:
Alright, so, in conclusion of a long-winded thread where I keep talking to
myself, I must say I am horrified by Microsoft's inconsistency.
You've only just scratched the surface yet, and there's som *ugly* old
paint down there related to finalizers which shines through as
Dispose(bool).
Finalization is not ordered wrt. referencing (think about it, how would
you finalize a cyclic chain if it was -- but it's not even ordered for
non-cyclic reference chains).
Because of that, a class with a finalizer (and methods invoked by it)
cannot (safely) use the objects its object references, since they may
already have been finalized.
Many System.* classes (for example Stream) implement IDisposable
"solving" this using the "Dispose(bool)" pattern
(
http://msdn2.microsoft.com/en-us/library/fs2xkftw.aspx):
class Foo: IDisposable {
IDisposable.Dispose() {
Dispose(true);
}
protected void Dispose(bool disposing) {
if ( disposing )
// dispose managed resources
// dispose unmanaged resources
}
~Foo() { Dispose(false); }
}
Even more unnerving is the way that object finalization elegibility is
interpreted. Since the finalizer runs on a separate thread an object may
be finalized while a method is still being invoked on it! See
http://msdn2.microsoft.com/en-us/library/system.gc.keepalive.aspx.
The "fix" is to GC.KeepAlive(o), but compilers are allowed to optimize
that away in situations where it can prove that GC.KeepAlive is never
run (making the refernce to o dead), for example in:
Foo o = new Foo();
return o.f(); // o may be finalized while o.f is still running
GC.KeepAlive(o);
so to fix *that* you need to write your code in a way that prevents the
compiler from proving that GC.KeepAlive runs, for example:
public static volatile bool fool_the_compiler = true;
Foo o = new Foo();
if ( fool_the_compiler )
return o.f();
GC.KeepAlive(o);
throw new InvalidOperationException(); // must return or throw
Happy hacking
.....
Well, fortunatly it's not a problem very often in the real world. but it
stinks just the same.