Previous month:
December 2004
Next month:
February 2005

January 2005

Dispose and Finalize with C++/CLI, C# and VB

Depending on the .NET language more or less of implementing resource management with the interface IDisposable and the finalizer is hidden. Interestingly, with VB nothing is hidden - however all the code is created automatically with Visual Studio 2005 as soon as the interface IDisposable is implemented.

The syntax implementing the Dispose method of the IDisposable interface and overriding the Finalize method of the Object class is shown with this table:

IL C++/CLI C# VB
Dispose ~ClassName Dispose Dispose
Finalize !ClassName ~ClassName Finalize

Visual Basic does not hide Dispose and Finalize. The automatically created code (with Visual Studio 2005) also includes the Dispose(bool) pattern:

Public Class Resource
    Implements IDisposable

    Private disposed As Boolean = False

    ' IDisposable
    Private Overloads Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposed Then
            If disposing Then
                ' TODO: put code to dispose managed resources
            End If

            ' TODO: put code to free unmanaged resources here
        End If
        Me.disposed = True
    End Sub

#Region " IDisposable Support "
    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Overloads Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overrides Sub Finalize()
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(False)
        MyBase.Finalize()
    End Sub
#End Region

End Class

Implementing a similar functionality with C#, the destructor syntax is used to override the Finalize method. The IDisposable interface and the Dispose pattern must be implemented traditionally.

class Resource : IDisposable
{
  private bool disposed = false;

  ~Resource()
  {
    Dispose(false);
  }

  protected void Dispose(bool disposing)
  {
    if (disposing)
    {
      // dispose managed resources
    }
    // dispose unmanaged resources

    disposed = true;
  }

  #region IDisposable Members
 
public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }
 
#endregion

  public void Foo()
  {
    if (disposed)
      throw new ObjectDisposedException("Object already disposed");
    //...

  }
}

C++/CLI not only hides the implementation of the Finalize method, but also the implementation of the IDisposable interface with the Dispose method. Contrary to C# where the destructor is used to implement the finalizer, here the destructor is used to implement IDisposable. Calling this method is done with the delete operator. The destructor syntax automatically includes code for GC.SuppressFinalize(this).

To override the Finalize method, a new syntax is used: !Class().

The Dispose(bool) pattern still must be done.

ref class Resource
{
private:
  bool disposed;
public:
  Resource()
  {
    disposed = false;
  }

  ~Resource() // IDisposable
  {
    Dispose(true);
  }

protected:
  !Resource() // Finalize
  {
    Dispose(false);
  }

  void Dispose(bool disposing)
  {
    if (disposing)
    {
      // dispose managed resources
    }
    // dispose unmanaged resources

    disposed = true;
  }
};

Christian


C++/CLI and IDisposable

With my previous post about Instantiating Managed Objects with C++/CLI you could read about using ref types two different ways: with local variables and with a handle.

Now I'm adding implementing the IDisposable interface with the Test class:

ref class Test
{
public:
    ~Test()
    {
        Console::WriteLine("destructor");
    }

    void Foo()
    {
        Console::WriteLine("Inner.Foo");
    }
};

With C++/CLI the IDisposable interface is implemented with the destructor syntax.

Now it would be good to do a try/finally to dispose the object. With C++/CLI the object is disposed using delete.
Test^ t = gcnew Test();
try
{
    t->Foo();
}
finally
{
    delete t;
}

With local variables the same can be done with fewer lines of code. This is shorter than the C# using statement!

Test t;
t.Foo();

The generated code is nearly the same. Nearly as in the first version the IL code uses the IL .try/finally statements, whereas the second version uses IL .try/fault and disposes the object in the fault and the default case.

Christian


C++/CLI: Instantiating Managed Objects

C++/CLI (not Beta 1) allows declaring reference types as local variables. Let's start with this simple ref class:

ref class Test
{
public:
    void Foo()
    {
        Console::WriteLine("Foo");
    }
};

Handles and the gcnew operator can be used to create instances:
Test^ t = gcnew Test();
t->Foo();

And without using a handle - this looks like a value type (this is new with Visual Studio 2005 versions after Beta 1):
Test t;
t.Foo();

What's the difference with the generated IL code? Nothing! Using a reference type as a local variable creates an object on the heap the same way as the version with the local variable. A reference type is not put on the stack.
A special advantage using local variables is with classes that implement IDisposable as you can read in my next weblog entry.

Christian