我一直在寻找一种方法,以确保在所有情况下(例如在类构造函数末尾的异常)都清除类的成员变量。
因为它们是成员变量,所以“尝试,捕获”和“使用”模式没有用。我注意到.NET C ++(C ++ / clr:safe)提供了对智能指针(称为msclr :: auto_handle)的仿真,例如auto_ptr或shared_ptr。这非常有用,因为我可以非常干净地确定性地破坏有限资源,例如线程或套接字。
我一直在分析用C ++ / clr生成的IL,并注意到它实际上所做的就是在修改封装数据的每个函数中都通过try / faults向IL垃圾邮件。
我已经为有兴趣的人列出了IL清单。(try / fault不是我添加的,而是C ++ / clr编译器添加的)
MyClass()
{
myDisposable.reset(gcnew MyDisposable());
throw gcnew Exception("Hello World");
// myDisposable needs to clean up now
// because it is very large or locks a limited resource.
// Luckily with RAII.. it does!
}
Run Code Online (Sandbox Code Playgroud)
...变成...
.try
{
IL_0006: ldarg.0
IL_0007: ldloc.0
IL_0008: stfld class msclr.'auto_handle<MyDisposable>' modreq([mscorlib]System.Runtime.CompilerServices.IsByValue) MyClass::myDisposable
IL_000d: ldarg.0
IL_000e: call instance void [mscorlib]System.Object::.ctor()
IL_0013: ldarg.0
IL_0014: ldfld class msclr.'auto_handle<MyDisposable>' modreq([mscorlib]System.Runtime.CompilerServices.IsByValue) MyClass::myDisposable
IL_0019: newobj instance void MyDisposable::.ctor()
IL_001e: call instance void msclr.'auto_handle<MyDisposable>'::reset(class MyDisposable)
IL_0023: ldstr "Hello World"
IL_0028: newobj instance void [mscorlib]System.Exception::.ctor(string)
IL_002d: throw
IL_002e: leave.s IL_003c
} // end .try
fault
{
IL_0030: ldarg.0
IL_0031: ldfld class msclr.'auto_handle<MyDisposable>' modreq([mscorlib]System.Runtime.CompilerServices.IsByValue) MyClass::myDisposable
IL_0036: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003b: endfinally
} // end handler
Run Code Online (Sandbox Code Playgroud)
是否有类似的方法可以使用C#实现此目标,因为我的软件将非常复杂,因此我自己处理所有这些问题将非常危险且容易出错。那么,有谁知道一种技术或什至可以自动添加此额外的IL代码的后期构建步骤,从而使我可以使用C#模拟RAII吗?
编辑:(另一个示例)
ref class MyClass
{
private:
msclr::auto_handle<MyDisposable> myDisposable;
public:
MyClass()
{
myDisposable.reset(gcnew MyDisposable());
throw gcnew Exception("Hello World");
// myDisposable needs to clean up now because it is very large or locks a limited resource.
}
};
Run Code Online (Sandbox Code Playgroud)
myDisposable是成员变量。从构造函数中抛出“ Hello World”时,myDisposable实际上立即被处置。我可以在C#中获得相同的功能吗?我们已经确认使用无法正常工作,因为它位于成员变量上,并且每个函数中的try / catch都是非常差的解决方案。
最好的祝福,
卡斯滕
C ++ / CLI中的auto_handle <>模板类使用特定于C ++ / CLI编译器的功能,称为“堆栈语义”。C ++程序员非常熟悉的功能以及RAII背后的核心运行时支持。简而言之,编译器确保在作用域块的末尾调用析构函数。您会在IL中看到.try / fault块,以确保即使在代码引发异常时也可以调用析构函数。
auto_handle类的析构函数调用它包装的对象的析构函数。这就是与C ++的相似之处结束的地方,C ++ / CLI类的析构函数是IDisposable.Dispose()方法。“实际”析构函数是该类的终结器,由!classname语法表示。
并且与C#的相似性开始了,它与using语句完全相同。它也确保了Dispose()方法被调用,并且还使用try / finally来确保即使在有异常的情况下也会发生这种情况。设计人员没有选择向C ++ / CLI 添加using关键字,而是选择了C ++程序员更熟悉的语法。从IDisposable的用法中还可以看出,您不能调用Dispose(),而必须使用delete运算符来调用它。
所以,如果你喜欢auto_handle <在C ++ / CLI>那么你就会有相同的原因,就像用在C#。
请注意C ++中的RAII与auto_handle /在托管代码中使用之间的巨大区别。您通常需要RAII以C ++释放内存,这在托管语言中是完全不必要的。也不能,这是垃圾收集器的工作。仅在创建继承IDisposable的对象时才应使用using。当然,.NET中不是每个类都可以。它也是可选的,不是必需的,类的终结器始终确保在未使用Dispose()尽早完成非托管资源时将其释放。
在特殊情况下,由于粗暴的异常处理或在跟踪对象生命周期上的痛苦太大,跳过Dispose()调用当然是可以的。.NET框架中此类的标准示例是Thread类。它具有5个一次性本机资源,但未实现IDisposable。因为编写代码以确保代码被调用往往会破坏使用线程的意义。
| 归档时间: |
|
| 查看次数: |
727 次 |
| 最近记录: |