作为一名C++开发人员,Java和.NET中缺少RAII(资源获取是初始化)一直困扰着我.清理的责任从班级作者转移到其消费者(通过try finally
.NET的using
构造)这一事实似乎显然是劣等的.
我明白为什么在Java中不支持RAII,因为所有对象都位于堆上,而垃圾收集器本身并不支持确定性破坏,但在.NET中引入了values-types(struct
)我们有(看似) RAII的完美候选人.在堆栈上创建的值类型具有明确定义的范围,并且可以使用C++析构函数语义.但是,CLR不允许值类型具有析构函数.
我的随机搜索发现了一个论点,如果一个值类型被装箱,它就属于垃圾收集器的管辖范围,因此它的破坏变得不确定.我觉得这个论点不够强大,RAII的好处足以说明带有析构函数的值类型不能被装箱(或用作类成员).
简而言之,我的问题是:为了将RAII引入.NET,是否有任何其他原因无法使用值类型?(或者您认为我关于RAII明显优势的论点有缺陷吗?)
编辑:我必须没有明确表达这个问题,因为前四个答案已经错过了重点.我知道关于Finalize
其不确定性的特点,我知道的using
结构,我觉得这两个选项都逊色于RAII.using
是一个阶级的消费者必须记住的另一件事(有多少人忘了把它StreamReader
放在一个using
街区?).我的问题是关于语言设计的哲学问题,为什么它是这样的,它可以改进吗?
例如,对于通用的确定性可破坏的值类型,我可以使using
和lock
关键字冗余(可以通过库类实现):
public struct Disposerwhere 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(); } }
我忍不住以我曾经看过的apropos报价结束,但目前无法找到它的起源.
当我的冷死手超出范围时,你可以采取我的确定性破坏.- 匿名
Adam Wright.. 16
一个更好的标题是"为什么C#/ VB中没有RAII".C++/CLI(Managed C++的堕胎的演变)具有与C++完全相同的RAII.对于与其他CLI语言相同的终结模式而言,它只是语法糖(C++/CLI的托管对象中的析构函数是有效的终结器),但它就在那里.
您可能喜欢http://blogs.msdn.com/hsutter/archive/2004/07/31/203137.aspx
一个更好的标题是"为什么C#/ VB中没有RAII".C++/CLI(Managed C++的堕胎的演变)具有与C++完全相同的RAII.对于与其他CLI语言相同的终结模式而言,它只是语法糖(C++/CLI的托管对象中的析构函数是有效的终结器),但它就在那里.
您可能喜欢http://blogs.msdn.com/hsutter/archive/2004/07/31/203137.aspx
优秀的问题和一个困扰我的人.似乎RAII的好处有很大不同.根据我使用.NET的经验,缺乏确定性(或至少是可靠的)资源收集是主要缺点之一.实际上,.NET迫使我多次使用整个架构来处理可能(但可能不需要)显式收集的非托管资源.当然,这是一个巨大的缺点,因为它使整体架构更加困难,并将客户的注意力从更加核心的方面引导.
Brian Harry在这里有一篇关于理由的好文章.
这是一段摘录:
那么确定性的最终化和价值类型(结构)呢?
--------------我已经看到很多关于具有析构函数等结构的问题.这值得评论.对于某些语言没有它们的原因,存在各种各样的问题.
(1)组成 - 由于上述相同的组成原因,它们在一般情况下不给出确定性寿命.任何包含一个非确定性类的非确定性类都不会调用析构函数,直到它最终由GC完成.
(2)复制构造函数 - 它真正好的一个地方是堆栈分配的本地化.他们将采用该方法的范围,一切都会很棒.不幸的是,为了使其真正起作用,您还必须添加复制构造函数,并在每次复制实例时调用它们.这是关于C++最丑陋,最复杂的事情之一.您最终会在您不期望的地方执行代码.它会导致一堆语言问题.一些语言设计师选择远离这一点.
假设我们使用析构函数创建了结构,但添加了一些限制,以使其行为在上述问题面前变得合理.限制将是这样的:
(1)您只能将它们声明为局部变量.
(2)你只能通过-ref传递它们
(3)您无法分配它们,您只能访问它们并调用方法.
(4)你不能装箱子.
(5)通过反射(后期绑定)使用它们的问题,因为这通常涉及拳击.
也许更多,但这是一个良好的开端.
这些东西会有什么用处?你真的会创建一个只能用作局部变量的文件或数据库连接类吗?我不相信任何人真的会这样.你要做的是创建一个通用连接,然后创建一个自动破坏的包装器,用作作用域的局部变量.然后呼叫者将选择他们想要使用的内容.请注意,调用者做出了决定,并未将其完全封装在对象本身中.鉴于您可以使用类似于几个部分中提出的建议.
在.NET中替换RAII就是使用模式,一旦你习惯了它,它几乎也可以工作.