问题:Java / C#为什么不能实现RAII?
澄清:我知道垃圾收集器不是确定性的。因此,使用当前的语言功能,不可能在范围出口处自动调用对象的Dispose()方法。但是,可以添加这种确定性功能吗?
我的理解:
我认为RAII的实现必须满足两个要求:
1.资源的生存期必须绑定到范围。
2.隐式的。必须在没有程序员明确声明的情况下释放资源。类似于垃圾回收器无需显式语句即可释放内存。“隐含性”仅需要在使用该类时发生。当然,类库创建者必须显式实现析构函数或Dispose()方法。
Java / C#满足点1。在C#中,可以将实现IDisposable的资源绑定到“使用”范围:
void test()
{
using(Resource r = new Resource())
{
r.foo();
}//resource released on scope exit
}
这不满足第2点。程序员必须将对象明确绑定到特殊的“使用”范围。程序员可能(并且确实)忘记将资源显式绑定到作用域,从而造成泄漏。
实际上,“使用”块已由编译器转换为try-finally-dispose()代码。它具有与try-finally-dispose()模式相同的显式性质。如果没有隐式发布,则作用域的钩子就是语法糖。
void test()
{
//Programmer forgot (or was not aware of the need) to explicitly
//bind Resource to a scope.
Resource r = new Resource();
r.foo();
}//resource leaked!!!
我认为值得用Java / C#创建语言功能,以允许通过智能指针将特殊对象挂接到堆栈上。该功能将允许您将一个类标记为作用域绑定,以便始终使用与堆栈的挂钩来创建该类。可能有不同类型的智能指针的选项。
class Resource - ScopeBound
{
/* class details */
void Dispose()
{
//free resource
}
}
void test()
{
//class Resource was flagged as ScopeBound so the tie to the stack is implicit.
Resource r = new Resource(); //r is a smart-pointer
r.foo();
}//resource released on scope exit.
我认为隐式是“值得的”。正如垃圾回收的隐含性“值得”一样。显式使用块令人耳目一新,但与try-finally-dispose()相比,没有提供语义优势。
在Java / C#语言中实现这样的功能是否不切实际?可以在不破坏旧代码的情况下引入它吗?
using
的执行是Dispose
有保证的(嗯,取消进程突然死掉而不会引发异常,这时所有清理可能会变得毫无意义)。
struct
),但除非在非常特殊的情况下,否则通常避免使用它们。另请参阅。
Dispose
会一直运行,无论它们是如何触发的。在作用域末尾添加隐式销毁将无济于事。