C++/CLI 教程:从 C# 可释放类派生





5.00/5 (24投票s)
本文介绍了从可释放的 C# 基类实现 C++/CLI 类。
引言
上周在工作中,我需要处理一个从 C# 编写的类派生的 C++/CLI 类,该类实现了 IDisposable
。 一开始我弄错了(是的,就是几年前写过关于这个主题的书的那个人),我的老板和我花了一些时间在 Reflector 中查看生成的代码,然后才对其进行修复。 那天晚上我回到家,很快就组装了一个简单的项目,以便我可以从一个简单的角度看到整个画面。 我认为发布一篇反映这个问题(并非有意双关)的文章会对其他人有所帮助。
C# 基类
这是一个实现了 IDisposable
的简单 C# 类。 假设该类处理托管和本机资源。
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);
}
}
}
对于任何从该类继承的 C# 开发人员来说,根据类的要求重写和实现 void Dispose(bool)
是轻而易举的。 但是,从 C++/CLI 来看,情况并非如此。 C++/CLI 不允许您的类中有任何名为“Dispose
”的方法。 虽然您可能之前已经在 C++/CLI 中实现了 IDisposable
类,但您可能还没有从 C# IDisposable
基类派生过。 无论如何,诀窍是避免过度思考和使问题复杂化 - 您只需要像往常一样处理它即可。 将托管释放放在析构函数中,将本机释放放在终结器中。
C++/CLI 派生类
这是 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)
{
}
};
}
乍一看,代码似乎是错误的。 我们如何确保在需要时调用基类释放代码(和终结器)? 让我们编写一些测试代码来看看会发生什么。
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;
}
运行结果如下:-
哇。 一切正常。 这是怎么发生的? 答案在于 VC++ 编译器魔术。 使用 Reflector 查看生成的代码向我们表明,编译器已经生成了我们可能不得不自己编写的代码。
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
调用期间,将发生以下情况
- C# 基类
Dispose
将调用Dispose(true)
并禁止 GC。 - C++/CLI 实现将调用
~DisposableDerived
(它释放派生类中的托管资源,然后调用!DisposableDerived
,它释放派生类中的本机资源)。 - 现在,调用
base.Dispose(true)
(在finally
块中),它释放基类中的托管和本机资源。
这就是终结期间发生的情况
- C# 基类终结器调用
Dispose(false)
。 - C++/CLI 实现调用
!DisposableDerived
(它释放派生类中的本机资源)。 - 并且调用
base.Dispose(false)
(在finally
块中),它释放基类中的本机资源。
本质上,这段代码正是我们需要编写的 - 编译器为我们生成了它。
结论
我知道这是一篇非常简单的文章,但有时我们会被简单的事情绊倒。 我希望我在我的书中涵盖了这个主题(我在书中谈到了确定性销毁)。 与往常一样,请随时提交反馈(批判或其他方式)。
历史
- 2008 年 7 月 5 日 - 文章发布。