我正在将一些代码从 C++/CLI 转换为 C#。其中一个对象在 C++/CLI 版本中有一个析构函数。使用后,其他一些 C++/CLI 代码在此对象上调用“删除”。
我需要在该对象的 C# 版本中实现哪种方法,以便那些“删除”继续发挥相同的功能(IDisposable.Dispose、终结器或我缺少的其他东西)?
如果您需要确定性地处理资源,我会说IDisposable
界面就是您所寻找的。这通常是非托管资源的情况,例如需要关闭的非托管句柄、流或数据库连接。
在 C++/CLI 中,如果声明托管类型(ref class
等),IDisposable
则使用析构函数语法实现,并Dispose()
使用delete
关键字调用。如果您在本地声明此类托管类型的对象(不使用^
运算符 or gcnew
),C++/CLI 甚至Dispose()
会在对象超出范围时自动调用您。这样一来,C++/CLI 比 C# 更方便。
使用 C# 时您将无法调用delete
该对象,您需要Dispose()
手动调用它。处理IDisposable
对象的另一种方法是using
块。
终结器(在 C# 中使用析构函数语法实现)与 C++ 析构函数不同,因为它在何时被调用是不确定的。带有终结器的对象基本上会排队,直到终结器线程决定调用它们的终结器,因此实际上您永远不知道确切的调用时间。
处理非托管资源的最佳方法可能是两者的结合。请参阅此处了解推荐的方法:http:
//msdn.microsoft.com/en-us/library/b1yfkh5e (v=vs.100).aspx
但是请注意,在使用 时IDisposable
,即使您可以确定性地处置非托管资源,托管对象仍需要由垃圾收集器(非确定性地)收集。
我刚刚发现一篇文章解释了 C++/CLI 和 C# 之间的区别。您可能会觉得它很有趣:
http ://weblogs.thinktecture.com/cnagel/2006/04/ccli-finalize-and-dispose.html
C++/CLI 和 C# 之间存在术语不匹配。C++/CLI 析构函数 (~Foo) 是 IDisposable.Dispose() 方法实现。C++/CLI 类没有显式实现 IDisposable,它是自动从析构函数的存在开始的。
C++/CLI 终结器 (!Foo) 是 C# 析构函数。
所以 C++/CLI删除操作符相当于调用 Dispose() 方法。当心 C++/CLI 中的堆栈语义,由不带 ^ 帽子的引用类型的局部变量触发。编译器在作用域块的末尾生成对 Dispose() 的隐藏调用。这相当于 C# using关键字。很难从源代码中看到,因此请特别注意或使用 ildasm.exe 查看生成的 IL
C# 没有提供相同的工具,但它确实为您提供了模式:如果您的 dtor 正在关闭流、取消链接指针或适当地设置状态,那么我觉得该IDisposable
接口非常适合:
// C#
void Dispose()
{
_stream.Close();
_ptr = null;
_running = false;
}
// with
obj.Dispose();
您不能真正强制 GC 保持内存打开。有一些方法可以帮助 GC 了解可以和应该释放的内容,请阅读http://msdn.microsoft.com/en-us/library/ee787088.aspx了解更多信息。
请记住, usingIDisposable
期望您适当地调用该Dispose()
方法。所以代替每个delete
,你会想要使用Dispose()
. 终结器的安全部分是,当 GC 实际释放它时,它会被调用。但是,您不能自己调用终结器(因此,如果删除/处置的时间很重要,那不是正确的解决方案。)
对我来说,这真的取决于析构函数在做什么。如果它正在执行诸如释放非托管资源(例如 SQL 或文件连接)之类的操作,那么我将实现 IDispose 并在 Dispose() 方法中关闭连接。
如果它正在破坏不需要任何显式清理的其他对象,那么我只需将析构函数留在外面,让 GC 处理它。