作为一名C ++开发人员,Java和.NET中缺少RAII (Resource Acquisition Is Initialization)一直困扰着我。清理的责任从班级作者转移到其消费者(通过try finally
或.NET的using
construct)这一事实似乎明显逊色。
我明白为什么在Java中不支持RAII,因为所有对象都位于堆上,垃圾收集器本身不支持确定性破坏,但在.NET中引入了值类型({{1} })我们有(看似)完美的RAII候选人。在堆栈上创建的值类型具有明确定义的范围,并且可以使用C ++析构函数语义。但是,CLR不允许值类型具有析构函数。
我的随机搜索发现一个参数,如果值类型是boxed,它属于垃圾收集器的管辖范围,因此它的破坏变得不确定。 我认为这个论点不够强大,RAII的好处足以说明带有析构函数的值类型不能被装箱(或用作类成员)。
简而言之,我的问题是:是否有任何其他原因无法使用值类型来将RAII引入.NET? (或者你认为我关于RAII明显优势的争论有缺陷吗?)
编辑:由于前四个答案错过了重点,我一定没有明确表达过这个问题。我知道关于struct
及其非确定性特征,我知道Finalize
构造,我觉得这两个选项不如RAII。 using
是一个类的消费者必须记住的另一件事(有多少人忘记将using
放在StreamReader
区块中?)。我的问题是关于语言设计的哲学问题,它为什么会这样,可以改进吗?
例如,对于具有通用确定性可破坏值的类型,我可以使using
和using
关键字变为冗余(可由库类实现):
lock
我忍不住用我曾经看过的apropos报价结尾,但目前找不到它的来源。
当我冷酷的死手超出范围时,你可以采取我的确定性破坏。 - 匿名
答案 0 :(得分:16)
更好的标题是“为什么C#/ VB中没有RAII”。 C ++ / CLI(Managed C ++的堕胎的演变)具有与C ++完全相同的RAII。对于与其他CLI语言相同的最终化模式而言,它只是语法糖(C ++ / CLI的托管对象中的析构函数是有效的终结器),但它就在那里。
您可能希望http://blogs.msdn.com/hsutter/archive/2004/07/31/203137.aspx
答案 1 :(得分:14)
优秀的问题和一个让我很困扰的问题。似乎RAII的好处有很大不同。根据我使用.NET的经验,缺乏确定性(或至少是可靠的)资源收集是主要缺点之一。实际上,.NET迫使我多次使用整个架构来处理可能(但可能不需要)显式收集的非托管资源。当然,这是一个巨大的缺点,因为它使整体架构更加困难,并将客户的注意力从更加核心的方面引导出来。
答案 2 :(得分:14)
Brian Harry发表了关于理由here的好文章。
以下是摘录:
确定性终结和价值类型(结构)怎么样?
--------------我见过很多关于结构的问题 析构函数等。这是值得的 评论。有各种各样的 为什么某些语言没有的问题 拥有它们。
(1)作文 - 他们不给你 一般的确定性寿命 同一种成分的情况 上述原因。任何 包含一个的非确定性类 直到它才会调用析构函数 无论如何,GC最终确定了。
(2)复制构造函数 - 一个地方 它真的很棒的地方 堆栈分配的本地人。他们将是 范围到方法,所有将是 大。不幸的是,为了得到 这真的有用,你也必须这样做 添加复制构造函数并调用它们 每次复制实例。 这是最丑陋的一个 关于C ++的复杂事情。你最终 让代码在整个地方执行 你不指望它的地方。它 导致一堆语言问题。 一些语言设计师选择了 远离这个。
假设我们用。创建了结构 destructors但添加了一堆 限制他们的行为 面对这些问题是明智的 以上。限制将是 类似的东西:
(1)您只能将它们声明为本地 变量。
(2)你只能传递它们 通过-REF
(3)你不能分配它们 只能访问字段和调用 他们的方法。
(4)你不能打包 他们。
(5)使用它们的问题 反思(后期绑定)因为那个 通常涉及拳击。
也许更多, 但这是一个好的开始。
这些东西会有什么用处?将 你实际创建一个文件或 可以的数据库连接类 只能用作局部变量?一世 不相信任何人真的会。 你要做的是创造一个 通用连接然后 为...创建一个自动破坏的包装器 用作范围局部变量。该 然后来电者会选择他们的 想用。请注意来电者做了一个 决定并不完全 封装在对象本身中。 鉴于你可以使用一些东西 喜欢上面提出的建议 几个部分。
在.NET中替换RAII就是使用模式,一旦你习惯了,它几乎也能正常工作。
答案 3 :(得分:1)
最接近的是stackalloc运算符非常有限。
答案 4 :(得分:1)
如果你搜索它们有一些类似的线程,但基本上它归结为如果你想在.NET上使用RAII,只需实现一个IDisposable类型并使用“using”语句来获得确定性的Disposal。这样,许多相同的意识形态只能以稍微冗长的方式实现和使用。
答案 5 :(得分:1)
所有这些都可以在vb.net中很好地解决,而在C#中则不太好,但是对它们的一流支持会改善两种语言。
答案 6 :(得分:-3)
您可以使用finalize()方法在.net和java中执行RAII形式。在GC清理类之前调用finalize()重载,因此可以用于清除类绝对不应保留的任何资源(互斥锁,套接字,文件句柄等)。但它仍然不是确定性的。
使用.NET,您可以使用IDisposable接口和using关键字确定性地执行此操作,但这确实有一些限制(使用构造时,确定性行为需要使用,仍然没有确定性内存释放,不会在类中自动使用,等等)。
是的,我觉得有一个地方可以将RAII的想法引入.NET和其他托管语言,尽管确切的机制可以无休止地争论。我能看到的另一个替代方案是引入一个可以处理任意资源清理的GC(不仅仅是内存),但是当确定必须释放所述资源时你会遇到问题。