我已经阅读过在C#中处理对象/ IDisposable接口和析构函数,但对我来说他们似乎做了同样的事情?
两者有什么区别?为什么我会使用一个而不是另一个?实际上,在此示例(下面的链接)中,此代码使用IDisposable接口和析构函数:
http://msdn.microsoft.com/en-us/library/system.idisposable.aspx
评论说析构函数是否未使用终结代码,但我如何决定何时使用其中一个?
答案 0 :(得分:81)
我写了一篇相当深入的帖子,该帖子应该有助于解释终结者,IDisposable以及何时应该使用其中一个:http://gregbee.ch/blog/implementing-and-using-the-idisposable-interface
可能最相关的部分引用如下:
当您使用非托管资源时 如句柄和数据库 连接,你应该确保 他们被保留在最低金额 时间,使用原则 迟到并早发布。在C ++中 通常是释放资源 在析构函数中完成,这是 确定性地在这一点上运行 删除对象的位置。互联网 但是,运行时使用垃圾 收集器(GC)清理和回收 没有的对象使用的内存 更长的可达因为这是在一个 定期基础就意味着这一点 清理对象的是 不确定的。结果 这就是析构函数不存在 对于托管对象,因为没有 运行它们的确定性地方。
而不是析构函数,C#有 终结者由...实施 覆盖定义的Finalize方法 在基础Object类上(虽然C# 有点混乱地使用C ++ 析构函数语法〜对象为此)。 如果对象覆盖Finalize 方法然后而不是 当GC离开时由GC收集 范围,GC将其放在终结器上 队列。在下一个GC循环中全部 队列上的终结器被运行(在...上 当前的单线程 实现)和来自的内存 回收的最终物品。它的 很明显,你为什么不这样做 想要在终结者中做清理:它 需要两个GC周期来收集 对象而不是一个而且有一个 所有终结器都是单线程 每隔一个线程运行一次 暂停了,所以它会受到伤害 性能
所以,如果你没有析构函数,那么 你不想离开清理 终结者,那么唯一的选择就是 手动,确定,清洁 对象。输入IDisposable 提供标准的界面 用于支持此功能和 定义一个方法,Dispose, 你在哪里放入清理逻辑 物体。在最终使用时 块,此接口提供 等效功能 析构函数。最后的原因 代码中的块主要用于支持 IDisposable接口;这就是为什么 C ++只使用try / except 不需要最终阻止 析构函数。
答案 1 :(得分:21)
如果您的对象的用户忘记拨打IDisposable.Dispose
,则终结器会为您提供商机来处置非托管资源。
如果您的对象实现了IDisposable
,则对象的用户必须调用.Dispose
。你不有来清理用户的混乱;但这是件好事。
我的most popular answer on Stackoverflow从一开始就指引你为什么你有IDisposable,它应该做什么,你的终结者可以做什么,不该做什么。
这个答案融化了面孔
用于描述它:P
答案 2 :(得分:5)
在托管编程语言中使用析构函数(~Object())是最愚蠢的想法。 对于像C,C ++这样的非托管语言而言,它们使用RAII习惯用于具有析构函数,但对于像Java,C#这样的托管,这是非常有意义的。
Java Collection Framework的前任项目负责人Joshua Bloch已经指出,Java中的finalize()方法(相当于C#的C ++,如析构函数)的想法是有史以来最大的错误。与C#相同,Java中的finallize()为“new”提供了开销,因为它必须在分配期间添加到finallizer队列。此外,垃圾收集器必须在队列中弹出并运行finallize(),因此在gc期间的开销是两倍。
C#有许多增强功能,例如“using(IDisposable){}”,它不仅允许将IDisposable变量限制在“using”块的范围内,而且还保证它的清理。 我的问题是,为什么C#遵循相同的Java路径导致了很大的错误。 可能如果dotnet的开发在大约2003〜2005年之后开始,当时许多Java架构师发现了finallize()的谬误,那么错误就会被阻止。
一种语言的许多好主意经常转移到其他语言,如C#中的“IDisposable / using combo”,它在“try(object-to-dispose){}”语句中转移到Java 1.7。但是,语言架构师在从一个人转移到另一个人的过程中没有发现伪装成好主意的坏主意,实在太糟糕了。
我的建议是永远不要使用~Destructor()并坚持使用IDisposable / using combo,如果你需要手动清理非托管资源,比如数据库连接。