正如其他回答者所指出的那样,您是对的,处置代码不同的原因是因为它是 C++/CLI。
C++/CLI 使用不同的习惯用法来编写清理代码。
- C#:Dispose() 和 ~ClassName()(终结器)都调用 Dispose(bool)。
- C++/CLI:Dispose() 和 Finalize() 都调用 Dispose(bool),后者将调用 ~ClassName() 或 !ClassName()(分别为析构函数和终结器)。
- ~ClassName() 和 !ClassName() 由开发人员编写。
- 正如您所指出的, ~ClassName() 的处理方式与 C# 不同。在 C++/CLI 中,它保留为名为“~ClassName”的方法,而 C# 中的 ~ClassName() 被编译为
protected override void Finalize()
.
- Dispose()、Finalize() 和 Dispose(bool) 仅由编译器编写。当它这样做时,编译器会做一些你通常不应该做的事情。
为了演示,这里有一个简单的 C++/CLI 类:
public ref class TestClass
{
~TestClass() { Debug::WriteLine("Disposed"); }
!TestClass() { Debug::WriteLine("Finalized"); }
};
这是 Reflector 的输出,反编译为 C# 语法:
public class TestClass : IDisposable
{
private void !TestClass() { Debug.WriteLine("Finalized"); }
private void ~TestClass() { Debug.WriteLine("Disposed"); }
public sealed override void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
[HandleProcessCorruptedStateExceptions]
protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool disposing)
{
if (disposing)
{
this.~TestClass();
}
else
{
try
{
this.!TestClass();
}
finally
{
base.Finalize();
}
}
}
protected override void Finalize()
{
this.Dispose(false);
}
}
编辑
看起来 C++/CLI 比 C# 更好地处理构造函数异常。
我用 C++/CLI 和 C# 编写了测试应用程序,它们定义了一个 Parent 类和一个 Child 类,其中 Child 类的构造函数会引发异常。这两个类都有来自其构造函数、dispose 方法和终结器的调试输出。
在 C++/CLI 中,编译器将子构造函数的内容包装在 try/fault 块中,并在错误时调用父构造函数的 Dispose 方法。(我相信当异常被其他一些 try/catch 块捕获时执行错误代码,而不是在向上移动堆栈之前立即执行的 catch 或 finally 块。但我可能会错过一个微妙之处.) 在 C# 中,没有隐式的 catch 或故障块,因此 Parent.Dispose() 永远不会被调用。当 GC 开始收集对象时,两种语言都会调用子终结器和父终结器。
这是我在 C++/CLI 中编译的测试应用程序:
public ref class Parent
{
public:
Parent() { Debug::WriteLine("Parent()"); }
~Parent() { Debug::WriteLine("~Parent()"); }
!Parent() { Debug::WriteLine("!Parent()"); }
};
public ref class Child : public Parent
{
public:
Child() { Debug::WriteLine("Child()"); throw gcnew Exception(); }
~Child() { Debug::WriteLine("~Child()"); }
!Child() { Debug::WriteLine("!Child()"); }
};
try
{
Object^ o = gcnew Child();
}
catch(Exception^ e)
{
Debug::WriteLine("Exception Caught");
Debug::WriteLine("GC::Collect()");
GC::Collect();
Debug::WriteLine("GC::WaitForPendingFinalizers()");
GC::WaitForPendingFinalizers();
Debug::WriteLine("GC::Collect()");
GC::Collect();
}
输出:
父母()
孩子()
CppCLI-DisposeTest.exe 中发生了“System.Exception”类型的第一次机会异常
〜父母()
异常捕获
GC::收集()
GC::WaitForPendingFinalizers()
!孩子()
!父()
GC::收集()
查看 Reflector 输出,以下是 C++/CLI 编译器如何编译 Child 构造函数(反编译为 C# 语法)。
public Child()
{
try
{
Debug.WriteLine("Child()");
throw new Exception();
}
fault
{
base.Dispose(true);
}
}
为了比较,这是 C# 中的等效程序。
public class Parent : IDisposable
{
public Parent() { Debug.WriteLine("Parent()"); }
public virtual void Dispose() { Debug.WriteLine("Parent.Dispose()"); }
~Parent() { Debug.WriteLine("~Parent()"); }
}
public class Child : Parent
{
public Child() { Debug.WriteLine("Child()"); throw new Exception(); }
public override void Dispose() { Debug.WriteLine("Child.Dispose()"); }
~Child() { Debug.WriteLine("~Child()"); }
}
try
{
Object o = new Child();
}
catch (Exception e)
{
Debug.WriteLine("Exception Caught");
Debug.WriteLine("GC::Collect()");
GC.Collect();
Debug.WriteLine("GC::WaitForPendingFinalizers()");
GC.WaitForPendingFinalizers();
Debug.WriteLine("GC::Collect()");
GC.Collect();
return;
}
和 C# 输出:
父母()
孩子()
CSharp-DisposeTest.exe 中出现了“System.Exception”类型的第一次机会异常
异常捕获
GC::收集()
GC::WaitForPendingFinalizers()
〜孩子()
〜父母()
GC::收集()