When can I call Finalize in Dispose?

I was looking at the decompiled source code for the 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 that it was not a reflector, I opened the method in ILSpy. He generated a similar code.

I went to Google to confirm my new discovery. I found the documentation for Object.Finalize , and here is what he said:

Each Finalize implementation in a derived type must call an implementation of the Finalize base type. This is the only time that application code is allowed to call Finalize.

Now I have nothing to think about. Perhaps this is due to the fact that the DLL was compiled with C ++. (Note: I could not find the Dispose implementation. Maybe it was automatically generated.) This may be a special tutorial for the IDisposable.Dispose method. This may be a disadvantage of both decompilers.

Some observations:

  • I could not find the Dispose implementation in the source code. It may be generated automatically.
  • The reflector shows a method called ~ClassName . It seems that this method cannot actually be a finalizer, but a C ++ destructor or even a regular method.

Is this legal C #? If so, how is this case different? If not, what is really happening? Is it allowed in C ++ / CLI but not C #? Or is it just a glitch in the decompiler?

+7
source share
2 answers

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 ()
+5
source

Yes, you are looking at C ++ / CLI code. In short from explicitly calling the finalizer, the generic C ++ / CLI template, the [MarshalAs] attribute in the argument is a dead sacrifice.

C ++ / CLI works differently than C #, the IDisposable interface and layout template are fully processed in the language. You will never specify an interface name, nor can you use Dispose directly. A very typical example is the shell of a ref class, which wraps an unmanaged C ++ class. You can paste it into the C ++ / CLI class library and see the IL you get from this code:

 using namespace System; #pragma managed(push, off) class Example {}; #pragma managed(pop) public ref class Wrapper { private: Example* native; public: Wrapper() : native(new Example) {} ~Wrapper() { this->!Wrapper(); } !Wrapper() { delete native; native = nullptr; } }; 

An β€œexample” is a native class; the shell stores a pointer to it as a private member. The constructor creates an instance with a new statement. This is a new operator managed by gcnew. The ~ Wrapper () method declares a "destructor". This is actually a dispose method. The compiler generates two members, the protected Dispose (bool) element, the one you are viewing in your fragment and probably familiar to you as the implementation of a one-time template. And the Dispose () method, you should also see this. Note that it calls GC.SuppressFinalize () automatically, just as you would explicitly do in a C # program.

Element! Wrapper () is a finalizer, the same as a C # destructor. Calling it from the destructor is allowed and very often makes sense. This is in this example.

+1
source

All Articles