I was browsing the decompiled source code for a DLL in Reflector, and I came across this C# code:
protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool flag1)
{
if (flag1)
{
this.~ClassName();
}
else
{
base.Finalize();
}
}
My first reaction was “What? I thought you couldn’t call the finalizer manually!”
Note: The base type is object.
To be sure it wasn’t a Reflector quirk, I opened up the method in ILSpy. It generated similar code.
I went to Google to confirm my new discovery. I found the documentation for Object.Finalize, and this is what it said:
Every implementation of Finalize in a derived type must call its base type’s implementation of Finalize. This is the only case in which application code is allowed to call Finalize.
Now I don’t what to think. It might be because the DLL was compiled with C++. (Note: I couldn’t find the implementation of Dispose. Maybe it’s auto-generated.) It might be a special allowance for the IDisposable.Dispose method. It might be a flaw in both decompilers.
Some observations:
- I couldn’t find the implementation of Dispose in the source code. Maybe it’s auto-generated.
- Reflector shows a method named
~ClassName. It seems as though this method might not actually be the finalizer, but the C++ destructor, or even an ordinary method.
Is this legal C#? If so, what is different about this case? If not, what is actually happening? Is it allowed in C++/CLI, but not C#? Or is it just a glitch in the decompiler?
As the other answerers have noted, you’re correct, the reason the disposal code is different is because it’s C++/CLI.
C++/CLI uses a different idiom for writing cleanup code.
protected override void Finalize().To demonstrate, here’s a simple C++/CLI class:
Here’s the output from Reflector, decompiling to C# syntax:
Edit
It looks like C++/CLI handles constructor exceptions better than C#.
I wrote test apps in both C++/CLI and C#, which define a Parent class and a Child class, where the Child class’s constructor throws an exception. Both classes have debug output from their constructor, dispose method, and finalizer.
In C++/CLI, the compiler wraps the contents of the child constructor in a try/fault block, and calls the parent’s Dispose method in fault. (I believe the fault code is executed when the exception is caught by some other try/catch block, as opposed to a catch or finally block where it would be executed immediately, before moving up the stack. But I may be missing a subtlety there.) In C#, there’s no implicit catch or fault block, so Parent.Dispose() is never called. Both languages will call both the child & parent finalizers, when the GC gets around to collecting the objects.
Here’s a test app I compiled in C++/CLI:
Output:
Looking at the Reflector output, here’s how the C++/CLI compiler compiled the Child constructor (decompiling to C# syntax).
For comparison, here’s the equivalent program in C#.
And the C# output: