Last week at the PDC I talked to Anders Hejlsberg (btw, he's a really nice person).
I asked him about having a way to prevent boxing and he told me they had already considered
it and found too many problems with it (reflection, generics, more on that in another
post). I also told him that I felt that C# had only one design flaw: The destructor
syntax. I was reminded of this when I was working on some java.nio classes yesterday.
The Java Community Doesn't Understand Finalization
How's that for a section title? It's obviously a generalization, but I don't think
it is too far from the truth. To prepare for writing this entry I looked at finalization
in three books:
All three are excellent books. Highly recommended.
Addison-Wesley put online the relevant section of Peter's book here.
First, let me say that Peter is a very smart guy (I know him from the Colorado
Software Summit) and that this doesn't reflect on the quality of the rest of the
book, but I'm picking on him because part of this particular "praxis" demonstrates
the, IMO, typical misunderstanding of finalization in the Java community (and because
his book is one of the few Java books I own).
So, what is wrong with his code? He uses the finalize method to cleanup managed
objects that already have their own finalizer!
This practice is widely used in the Java libraries as well and it is a really bad
idea. At best it doesn't help (the ServerSocket and FileInputStream will have already
been finalized (see JLS 12.6.2
Finalizer Invocations are Not Ordered), or will be finalized soon anyway) and
at worst you create APIs that are very difficult to use (or code) because multiple
objects "own" the same resource.
The JLS doesn't say anything useful about finalization,
but does mention that you should always call super.finalize() in your finalizer (like
Peter does in his code).
THIS IS USELESS!
Why? All finalizable classes should extend java.lang.Object and be final, because
there is no reason for them not to be. Here is what a finalizable class should look
like.
final class FileHandle {
private int nativeHandle;
FileHandle(String filename) {
nativeHandle = nativeOpen(filename);
}
private static int nativeOpen(String filename);
synchronized void close() {
if(nativeHandle != 0) {
nativeClose(nativeHandle);
nativeHandle = 0;
}
}
private static native void nativeClose(int handle);
protected void finalize() {
close();
}
// example operation, the others are omitted
synchronized int read(...) {
return nativeRead(nativeHandle, ...);
}
private static native int nativeRead(int handle, ...);
}
Other than exposing the primitive operations that can be performed on the unmanaged
resource (thru the handle), the class should have no functionality. The class is package
private and used only by the public classes that actually implement the exposed API.
The class should never have any references to other objects.
This pattern of factoring out the finalizable resource into a separate class is discussed
in the Jones/Lins book. It seems that not enough people read it.
The above discussion is specific to Java and .NET finalization, there are other implementations
of finalization that do finalize in order. In such an implementation it can actually
be useful to use references to other objects in your finalize method, because you
know that the object you're using hasn't already been finalized.
Back To C#
So what's wrong with the C# destructor syntax? I think it encourages the mistake of
thinking of Finalize as a destructor (and thus using it to cleanup other managed objects).
Also, a "feature" of the C# destructor is that it always call the base class Finalize
method. I hope I've shown that this isn't very useful.
Anders agreed with me. "In retrospect we probably shouldn't have done that." (paraphrased
from memory).