A reader comments
re: Changes in Destructor Semantics in Support of Deterministic Finalization
I agree with Johan.
I use a pattern where all classes that need a finalizer must implement the Dispose pattern, and all classes with a Dipose method also have a finalizer; if you have one you must have the other. All clients using the class must call the Dispose method.
In the debug build if a finalizer ever gets called it throws an exception – it means that the client never honored the contract. In the release build we log it and continue.
About being safe during finalization…there are more issues than just which subsystems are still valid (e.g. it may not be safe to call Console.WriteLine during system shutdown), there are also issues related to thread safety. I believe it’s possible for a finalizer to run at the same time that the Dispose method is called.
BTW: I’m looking at this from a perspective of pure C#, not managed C++.
This is all fine and good, but it is not language design. Rather, it is an intelligent response to an absence of support within your selected language. That is, it is a policy, and the primary difficulties of any policy are: (a) how do you communicate that policy to a heterogeneous community of programmers? (b) how do you integrate that policy with the policies imposed by other libraries that your library may be combined with and which you cannot anticipate? and (c) how do you enforce your policy over time and large scale?
What our language has done is integrate the notions of Dispose (~R()) and Finalize (!R()) into the class mechanism itself: we have provided both a vocabulary and automated support that fits intuitively as an extension of the native constructor/destructor `initialization as resource acquisition’ design of ISO-C++. I think this is both truly elegant and superior to anything provided by other