11

我正在将一些代码从 C++/CLI 转换为 C#。其中一个对象在 C++/CLI 版本中有一个析构函数。使用后,其他一些 C++/CLI 代码在此对象上调用“删除”。

我需要在该对象的 C# 版本中实现哪种方法,以便那些“删除”继续发挥相同的功能(IDisposable.Dispose、终结器或我缺少的其他东西)?

4

4 回答 4

17

如果您需要确定性地处理资源,我会说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

于 2012-04-19T17:46:14.637 回答
4

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

于 2012-04-19T18:27:54.350 回答
1

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 实际释放它时,它会被调用。但是,您不能自己调用​​终结器(因此,如果删除/处置的时间很重要,那不是正确的解决方案。)

于 2012-04-19T17:51:19.783 回答
0

对我来说,这真的取决于析构函数在做什么。如果它正在执行诸如释放非托管资源(例如 SQL 或文件连接)之类的操作,那么我将实现 IDispose 并在 Dispose() 方法中关闭连接。

如果它正在破坏不需要任何显式清理的其他对象,那么我只需将析构函数留在外面,让 GC 处理它。

于 2012-04-19T17:47:13.760 回答