|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionLast week at work, I had to work on a C++/CLI class that derived from a class written in C# which implemented The C# base classHere's a simple C# class that implements namespace DisposeDemo
{
[SuppressMessage("Microsoft.Naming",
"CA1709:IdentifiersShouldBeCasedCorrectly",
MessageId = "Cs",
Justification = "Personal preference")]
public class CsDisposableBase : IDisposable
{
#region IDisposable Members
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~CsDisposableBase()
{
Dispose(false);
}
/// <summary>
/// Derived classes need to override this appropriately
/// </summary>
/// <param name="disposing">Indicates whether this is a dispose call
/// or a call invoked from the finalzier</param>
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
DisposeManagedResources();
}
FreeNativeResources();
}
#endregion
private void DisposeManagedResources()
{
this.WriteLine("Disposing managed resources in base class");
}
private void FreeNativeResources()
{
this.WriteLine("Freeing native resources in base class");
}
public CsDisposableBase(String name)
{
this.Name = name;
}
public String Name { get; private set; }
protected void WriteLine(String text)
{
Console.WriteLine("{0} : {1}", this.Name, text);
}
}
}
For any C# developer inheriting from this class, it's a no-brainer to override and implement The C++/CLI derived classHere's the derived class implementation in C++/CLI: namespace CppDisposable
{
public ref class DisposableDerived : CsDisposableBase
{
private:
void DisposeManagedResourcesDerived()
{
WriteLine("Disposing managed resources in derived class");
}
void FreeNativeResourcesDerived()
{
WriteLine("Freeing native resources in derived class");
}
public:
~DisposableDerived()
{
DisposeManagedResourcesDerived();
this->!DisposableDerived();
}
!DisposableDerived()
{
FreeNativeResourcesDerived();
}
DisposableDerived(String^ name) : CsDisposableBase(name)
{
}
};
}
At first glance, the code seems wrong. How do we ensure that the base class disposal code (and the finalizer) are called as and when needed? Let's write some test code to see what happens. int main(array<System::String ^> ^args)
{
CppDisposable::DisposableDerived object1(
"Object that gets disposed");
CppDisposable::DisposableDerived^ object2 =
gcnew CppDisposable::DisposableDerived("Object that gets GCd");
return 0;
}
Running that gives the following output :- Wow. Everything worked fine. How did that happen? The answer lies in VC++ compiler magic. A look at the generated code using Reflector shows us that the compiler has generated the code that we may otherwise have had to write ourselves. What Reflector showsHere's the generated code (as viewed in Reflector): protected override void Dispose([MarshalAs(UnmanagedType.U1)] bool flag1)
{
if (flag1)
{
try
{
this.~DisposableDerived();
}
finally
{
base.Dispose(true);
}
}
else
{
try
{
this.!DisposableDerived();
}
finally
{
base.Dispose(false);
}
}
}
So, during a
And this is what happens during a finalization:
Essentially, this code is exactly what we'd have needed to write - the compiler generates it for us. ConclusionI know this is a very simple article, but sometimes it's the simple things that we stumble over. I wish I had covered this topic in my book (where I talk about deterministic destruction). As always, please feel free to submit feedback (critical or otherwise). History
|
|||||||||||||||||||||||||||||||||||||||||||||||||||