当前位置:  开发笔记 > 编程语言 > 正文

为什么.NET中没有RAII?

如何解决《为什么.NET中没有RAII?》经验,为你挑选了3个好方法。

作为一名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街区?).我的问题是关于语言设计的哲学问题,为什么它是这样的,它可以改进吗?

例如,对于通用的确定性可破坏的值类型,我可以使usinglock关键字冗余(可以通过库类实现):

    public struct Disposer 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();
        }
    }

我忍不住以我曾经看过的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



1> Adam Wright..:

一个更好的标题是"为什么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,但C++/CLI析构函数不是终结器.他们实现`IDisposable`.它是C++/CLI堆栈语义语法,它是特殊的,因为它允许您编写统一代码来处理任何有限生命周期的对象,无论它是否实现了`IDisposable`,这在通用代码中特别有用.

2> Konrad Rudol..:

优秀的问题和一个困扰我的人.似乎RAII的好处有很大不同.根据我使用.NET的经验,缺乏确定性(或至少是可靠的)资源收集是主要缺点之一.实际上,.NET迫使我多次使用整个架构来处理可能(但可能不需要)显式收集的非托管资源.当然,这是一个巨大的缺点,因为它使整体架构更加困难,并将客户的注意力从更加核心的方面引导.



3> Rasmus Faber..:

Brian Harry在这里有一篇关于理由的好文章.

这是一段摘录:

那么确定性的最终化和价值类型(结构)呢?

--------------我已经看到很多关于具有析构函数等结构的问题.这值得评论.对于某些语言没有它们的原因,存在各种各样的问题.

(1)组成 - 由于上述相同的组成原因,它们在一般情况下不给出确定性寿命.任何包含一个非确定性类的非确定性类都不会调用析构函数,直到它最终由GC完成.

(2)复制构造函数 - 它真正好的一个地方是堆栈分配的本地化.他们将采用该方法的范围,一切都会很棒.不幸的是,为了使其真正起作用,您还必须添加复制构造函数,并在每次复制实例时调用它们.这是关于C++最丑陋,最复杂的事情之一.您最终会在您不期望的地方执行代码.它会导致一堆语言问题.一些语言设计师选择远离这一点.

假设我们使用析构函数创建了结构,但添加了一些限制,以使其行为在上述问题面前变得合理.限制将是这样的:

(1)您只能将它们声明为局部变量.

(2)你只能通过-ref传递它们

(3)您无法分配它们,您只能访问它们并调用方法.

(4)你不能装箱子.

(5)通过反射(后期绑定)使用它们的问题,因为这通常涉及拳击.

也许更多,但这是一个良好的开端.

这些东西会有什么用处?你真的会创建一个只能用作局部变量的文件或数据库连接类吗?我不相信任何人真的会这样.你要做的是创建一个通用连接,然后创建一个自动破坏的包装器,用作作用域的局部变量.然后呼叫者将选择他们想要使用的内容.请注意,调用者做出了决定,并未将其完全封装在对象本身中.鉴于您可以使用类似于几个部分中提出的建议.

在.NET中替换RAII就是使用模式,一旦你习惯了它,它几乎也可以工作.


布赖恩哈利错了 - 这些问题中的很多都是可以解决的; 例如,请参阅boost的指针容器.当然,它需要一些坚实的工作,但它是非常可以解决的.就此而言,几乎每个.NET arch都意识到你需要所有权的概念来使用IDisposable - 如果你需要*它,你也可以将它嵌入到语言中.解决方案可能很麻烦,但更好的是一个混乱的解决方案,而不是Java + .NET所采用的那种坚持不懈的方法.
与RAII不同,如果你将它用于/ everything /,那么'使用'语句变得笨拙.
推荐阅读
黄晓敏3023
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有