As the other respondents noted, you are right, the reason is that the removal code is different from the fact that it is C ++ / CLI.
C ++ / CLI uses another idiom to write cleanup code.
- C #: Dispose () and ~ ClassName () (finalizer) are both Dispose (bool) calls.
- All three methods are written by the developer.
- C ++ / CLI: Dispose () and Finalize () are both Dispose (bool) calls that will either call ClassName () or! ClassName () (destructor and finalizer, respectively).
- ~ ClassName () and! ClassName () are written by the developer.
- As you noted, ~ ClassName () is handled differently than in C #. In C ++ / CLI, it remains as a method named "~ ClassName", while ~ ClassName () in C # compiles as
protected override void Finalize() .
- Dispose (), Finalize () and Dispose (bool) are written exclusively by the compiler. Since this does, the compiler does what is not normally assumed.
To demonstrate here a simple C ++ / CLI class:
public ref class TestClass { ~TestClass() { Debug::WriteLine("Disposed"); } !TestClass() { Debug::WriteLine("Finalized"); } };
Here's the output from Reflector, decompiling to C # syntax:
public class TestClass : IDisposable { private void !TestClass() { Debug.WriteLine("Finalized"); } private void ~TestClass() { Debug.WriteLine("Disposed"); } public sealed override void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } [HandleProcessCorruptedStateExceptions] protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool disposing) { if (disposing) { this.~TestClass(); } else { try { this.!TestClass(); } finally { base.Finalize(); } } } protected override void Finalize() { this.Dispose(false); } }
Edit
It seems that C ++ / CLI handles constructor exceptions better than C #.
I wrote test applications in both C ++ / CLI and C # that define the parent class and the Child class, where the constructor of the Child class throws an exception. Both classes have debug output from their constructor, the dispose and finalizer methods.
In C ++ / CLI, the compiler wraps the contents of the child constructor in a try / fault block and calls the parent Dispose method in an error. (I believe that the error code is executed when the exception falls into some other try / catch block, as opposed to the catch or finally block, where it will be executed immediately before moving up the stack. But maybe I am missing subtleties.) In C # there is no implicit catch or fault block, so Parent.Dispose () is never called. Both languages ββwill call both child and parent finalizers when the GC is about to collect objects.
Here's a test application compiled in C ++ / CLI:
public ref class Parent { public: Parent() { Debug::WriteLine("Parent()"); } ~Parent() { Debug::WriteLine("~Parent()"); } !Parent() { Debug::WriteLine("!Parent()"); } }; public ref class Child : public Parent { public: Child() { Debug::WriteLine("Child()"); throw gcnew Exception(); } ~Child() { Debug::WriteLine("~Child()"); } !Child() { Debug::WriteLine("!Child()"); } }; try { Object^ o = gcnew Child(); } catch(Exception^ e) { Debug::WriteLine("Exception Caught"); Debug::WriteLine("GC::Collect()"); GC::Collect(); Debug::WriteLine("GC::WaitForPendingFinalizers()"); GC::WaitForPendingFinalizers(); Debug::WriteLine("GC::Collect()"); GC::Collect(); }
Output:
Parent ()
Child ()
A first chance exception of type 'System.Exception' occurred in CppCLI-DisposeTest.exe
~ Parent ()
Exception Caught
GC :: Collect ()
GC :: WaitForPendingFinalizers ()
! Child ()
! Parent ()
GC :: Collect ()
Looking at the output of Reflector, this is how the C ++ / CLI compiler compiled the Child constructor (decompilation to C # syntax).
public Child() { try { Debug.WriteLine("Child()"); throw new Exception(); } fault { base.Dispose(true); } }
For comparison, here is the equivalent program in C #.
public class Parent : IDisposable { public Parent() { Debug.WriteLine("Parent()"); } public virtual void Dispose() { Debug.WriteLine("Parent.Dispose()"); } ~Parent() { Debug.WriteLine("~Parent()"); } } public class Child : Parent { public Child() { Debug.WriteLine("Child()"); throw new Exception(); } public override void Dispose() { Debug.WriteLine("Child.Dispose()"); } ~Child() { Debug.WriteLine("~Child()"); } } try { Object o = new Child(); } catch (Exception e) { Debug.WriteLine("Exception Caught"); Debug.WriteLine("GC::Collect()"); GC.Collect(); Debug.WriteLine("GC::WaitForPendingFinalizers()"); GC.WaitForPendingFinalizers(); Debug.WriteLine("GC::Collect()"); GC.Collect(); return; }
And C # output:
Parent ()
Child ()
A first chance exception of type 'System.Exception' occurred in CSharp-DisposeTest.exe
Exception Caught
GC :: Collect ()
GC :: WaitForPendingFinalizers ()
~ Child ()
~ Parent ()
GC :: Collect ()