try finally
或.NET的using
construct将清理责任从类编写者转移到其使用者似乎明显不够好。我知道Java中为什么不支持RAII,因为所有对象都位于堆上,垃圾回收器本质上不支持确定性销毁,但是在.NET中,随着值类型(
struct
)的引入,我们有了(看似)完美的RAII候选对象。在堆栈上创建的值类型具有明确定义的作用域,并且可以使用C++析构函数语义。然而,CLR不允许值类型具有析构函数。我的随机搜索发现一个论点,即如果值类型boxed,则落入垃圾回收器的管辖范围,因此其销毁变得非确定性。我认为这个论点不够强,RAII的好处足以说明具有析构函数的值类型不能被装箱(或用作类成员)。
简而言之,我的问题是:是否有其他原因导致无法使用值类型来引入RAII到.NET?(或者您认为我的关于RAII明显优势的论点存在缺陷吗?)
编辑:由于前四个答案都没有理解问题的重点,我可能没有清楚地表达我的问题。我知道有关Finalize
及其非确定性特征的信息,我知道using
结构,但我认为这两个选项都不如RAII。 using
是类的使用者必须记住的另一件事情(多少人忘记在using
块中放置StreamReader
?)。我的问题是一种关于语言设计的哲学问题,为什么它是这样的,能否改进?
例如,对于一个通用的确定性可销毁值类型,我可以使using
和lock
关键字变得冗余(可以通过库类实现):
public struct Disposer<T> where T : IDisposable
{
T val;
public Disposer(T t) { val = t; }
public T Value { get { return val; } }
~Disposer() // Currently illegal
{
if (val != default(T))
val.Dispose();
}
}
我忍不住要以一句相关的引语结束,这是我曾经看过但现在无法找到其来源的引语。
当我的冷酷死亡之手超出范围时,你可以接管我的确定性毁灭。 --匿名
IDisposable
接口。这是因为C++/CLI堆栈语义语法是特殊的,因为它允许您编写统一的代码来处理任何有限生命周期的对象,无论它是否实现了IDisposable
接口,在泛型代码中特别有用。 - Ben Voigt