Search code examples
c#.netvisual-c++c++-clidispose

When does the C++/CLI compiler create which dispose pattern?


This may sound like a weird question but depending on something the compiler generates different content for the dispose methods in the case of a C++/CLI class extending a C# class which implements IDisposable.

(If you are interested in what the dispose pattern looks like I recommend reading this or this. The latter also has information on C++/CLI. And here is an article talking explicitly about deriving a C++/CLI class from a C# class which implements IDisposable.)

The problem is best illustrated by showing the generated IL code (extracted with Reflector). Version 1:

[HandleProcessCorruptedStateExceptions]
protected override void Dispose([MarshalAs(UnmanagedType.U1)] bool A_0)
{
    if (A_0)
    {
        try
        {
            this.~Foo();
        }
        finally
        {
            base.Dispose(true);
        }
    }
    else
    {
        try
        {
            this.!Foo();
        }
        finally
        {
            base.Dispose(false);
        }
    }
}

Version 2:

[HandleProcessCorruptedStateExceptions]
protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool A_0)
{
    if (A_0)
    {
        try
        {
            this.~Foo();
        }
        finally
        {
            base.Dispose();
        }
    }
    else
    {
        try
        {
            this.!Foo();
        }
        finally
        {
            base.Finalize();
        }
    }
}

Note that in the first version a base method Dispose(bool) is being called while in the second either Dispose() or Finalize() get called. Both of these have been generated by almost the same code. In the second case the keyword "virtual" was omitted when defining Dispose(bool) in the base class.

It seems like the compiler is trying to determine if the Dispose pattern is used correctly and generate appropriate code.

My problem is that I have a case where the base class looks like this:

  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool disposing)
  {
    if (disposing)
    {
    }
  }

but the generated code is Version 2. Now, the base Dispose() function calls Dispose(true) virtually on the derived class which in turn calls base.Dispose() and my stack overflows.

So when does the compiler generate each version of the code?


Solution

  • Version 1 of the code with Dispose(true) calls occurs when the base class has a non-private virtual method called Dispose(bool).

    Version 2 of the code with Dispose() occurs when the base class does not have a Dispose(bool) method.

    There is a Version 3 of the code that looks nearly identical to Version 2 that occurs when the base class has a non-virtual Dispose(bool) method. The only difference is that the C++ Dispose(bool) function also has the new keyword on it.

    I can get your situation to happen by doing the following:

    1. Make a C# class that implements IDisposable without a Dispose(bool) method.
    2. Do a full compile of the C# and C++ libraries.
    3. Modify the C# class to add a virtual Dispose(bool) method.
    4. Compile (but do NOT rebuild all).

    In this case the C# code is generated correctly but the C++ code does not adjust the Dispose(bool) function to be what it should

    The fix for this is very simple: A rebuild-all turns the broken Version 2 code into the proper Version 1 code.