在处理资源管理时,C#和C++/CLI提供了不同的机制来确保资源被正确释放。本文将探讨如何在这两种语言中实现IDisposable接口,并分析它们在资源管理方面的异同。
在C#中,实现IDisposable接口是一种常见的资源管理方式。以下是一个简单的C#类,它实现了IDisposable接口,并且处理了托管资源和非托管资源。
namespace DisposeDemo
{
public class CsDisposableBase : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~CsDisposableBase()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
DisposeManagedResources();
}
FreeNativeResources();
}
private void DisposeManagedResources()
{
Console.WriteLine("Disposing managed resources in base class");
}
private void FreeNativeResources()
{
Console.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);
}
}
}
在C#中,任何继承此类的开发者都需要重写并实现Dispose(bool)方法,以满足类的要求。
在C++/CLI中,实现IDisposable接口并不像在C#中那样直观。C++/CLI不允许类中有名为Dispose的方法。但是,可以通过以下方式来实现它:
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)
{
}
};
}
这段代码看起来可能有些问题,但如何确保基类的Dispose方法(以及析构函数)在需要时被调用呢?让写一些测试代码来看看会发生什么。
int main(array^ args)
{
CppDisposable::DisposableDerived object1("Object that gets disposed");
CppDisposable::DisposableDerived^ object2 = gcnew CppDisposable::DisposableDerived("Object that gets GCd");
return 0;
}
运行这段代码,会看到以下输出:
哇,一切都工作得很好。这是怎么回事呢?答案在于VC++编译器的魔法。使用Reflector查看生成的代码,可以看到编译器为生成了可能需要自己编写的代码。
以下是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);
}
}
}
在Dispose调用期间,以下情况会发生:
在最终化期间,以下情况会发生:
本质上,这段代码正是需要编写的——编译器为生成了它。