Close and Dispose - 调用哪个?
Posted
技术标签:
【中文标题】Close and Dispose - 调用哪个?【英文标题】:Close and Dispose - which to call? 【发布时间】:2010-09-08 19:59:37 【问题描述】:阅读了Is SqlCommand.Dispose enough? 和Closing and Disposing a WCF Service 的线程后,我想知道诸如SqlConnection 之类的类或从Stream 类继承的几个类之一,如果我关闭Dispose 而不是Close,这有关系吗?
【问题讨论】:
【参考方案1】:我想澄清一下这种情况。
根据 Microsoft 指南,在合适的情况下提供 Close
方法是一种很好的做法。 Here 是来自Framework design guidelines 的引用
考虑提供方法
Close()
,除了Dispose()
,如果关闭是该地区的标准术语。这样做时,重要的是使Close
实现与Dispose
相同 ...
在大多数情况下,Close
和 Dispose
方法是等效的。在SqlConnectionObject
的情况下,Close
和Dispose
之间的主要区别是:
应用程序可以调用
Close
更多 不止一次。没有例外是 生成。如果你调用了
Dispose
方法SqlConnection
对象状态将是 重置。如果您尝试调用任何 处理方法SqlConnection
对象,您将收到异常。
也就是说:
如果使用连接对象一 时间,使用Dispose
。 using
块将确保即使在发生异常时也会调用它。
如果必须重用连接对象,
使用Close
方法。
【讨论】:
@Chris,Close() 的文档说“它然后释放与连接池的连接,或者如果连接池被禁用,则关闭连接。”所以 Close() 应该足以防止连接池溢出。 @DavidHammond:你是对的。我要删除我之前的评论。 .Dispose() 是否也将连接释放回池中? 这是我十年来读到的关于这个主题的最好的论据。好点。 所以它是这样工作的 1.con.Open() con.Close();
2 con.Open(); // reuse
3. con.Dispose(); // use one time con.Open(); // error
跨度>
【参考方案2】:
像往常一样,答案是:视情况而定。不同的类以不同的方式实现IDisposable
,这取决于您进行必要的研究。
就SqlClient
而言,推荐的做法是执行以下操作:
using (SqlConnection conn = /* Create new instance using your favorite method */)
conn.Open();
using (SqlCommand command = /* Create new instance using your favorite method */)
// Do work
conn.Close(); // Optional
您应该在连接上拨打Dispose
(或Close
*)! 不要等待垃圾收集器清理您的连接,这将占用池中的连接直到下一个 GC 周期(至少)。如果您调用Dispose
,则无需调用Close
,并且由于using
构造使得正确处理Dispose
变得如此容易,因此确实没有理由调用Close
。
连接是自动池化的,在连接上调用Dispose
/Close
不会物理关闭连接(正常情况下)。不要尝试实现自己的池。 SqlClient
在从池中检索连接时对连接执行清理(如恢复数据库上下文和连接选项)。
*如果您正在调用 Close
,请确保以异常安全的方式(即在 catch 或 finally 块中)进行。
【讨论】:
当你说,“这取决于你做必要的研究”,那是什么研究?我知道如何肯定地说的唯一方法是通过反射,但在大多数情况下,这具有“非法”的缺点。 我不会说:conn.Close(); // Optional
这不是可选的。这是多余的和不必要的。您正在处理该对象两次,这将被某些代码分析工具标记为警告。
@Metalogic 我同意使用正确的用法调用 Close 是多余的和不必要的(而且丑陋的)。但是,吹毛求疵:调用 Close 不是“处置”(而 Dispose 暗示 SqlConnection 的 Close)。与 using (var x = ..) x.Dispose();
相比,在这种情况下 x
确实被“处理了两次”。【参考方案3】:
您确实需要调用 Dispose()!
Dispose()是给开发者调用的,垃圾回收器调用Finalize()。如果您不对对象调用 Dispose(),则在垃圾收集器出现并对它们调用 finalize 之前,它们使用的任何非托管资源都不会被释放(谁知道什么时候会发生)。
这种情况称为非确定性终结,是 .net 开发人员的常见陷阱。如果您正在使用实现 IDisposable 的对象,请在它们上调用 Dispose()!
http://www.ondotnet.com/pub/a/oreilly/dotnet/news/programmingCsharp_0801.html?page=last
虽然可能有很多实例(例如在 SqlConnection 上)在某些对象上调用 Disponse(),而它只是在其连接上调用 Close() 或关闭文件句柄,几乎总是最好的选择是调用处置()!除非您打算在不久的将来重新使用该对象。
【讨论】:
此评论完全错误。垃圾收集器永远不会调用Dispose
。
推论:您应该调用Dispose()
如果您没有将using()
与实现IDisposable
的类一起使用。如果被调用的类实现了 IDisposable 并且您已将其用法包装在 using()
内的页面上,那么您可以使用 Dispose()
进行处理(双关语,所以开枪吧)。但是,对于任何明确使用 Open()
、AFAIK 的内容,建议使用 Close()
。
我不确定其他 DBMS,但您不能在 PostgreSql 中同时进行这两种操作。一旦你Close
一个连接,Postgres 自动将连接标识符设置为null
。从那里开始,不能Dispose
一个已经设置为null
的sql 连接标识符。【参考方案4】:
对于SqlConnection
,从连接本身的角度来看,它们是等价的。根据 Reflector 的说法,Dispose()
调用 Close()
以及执行一些额外的内存释放操作——主要是通过将成员设置为 null。
对于 Stream,它们实际上是等价的。 Stream.Dispose()
只需调用 Close()。
【讨论】:
你确定吗? MSDN 说it's inherited fromComponent
哪个doesn't seem to do anything to try and call Close()
。我在DBConnection
或SqlConnection
中看不到与这些通知中的任何一个相关的任何地方。但是它确实有一个私人DisposeMe()
that isn't referenced anywhere。
@Deanna 在这里被覆盖:github.com/dotnet/corefx/blob/…
@DavidCumps 自从我写那条评论以来,它似乎在 4 年内发生了变化。我的链接不再有效。
github.com/microsoft/referencesource/blob/master/System.Data/…,这里没看到【参考方案5】:
这个可能很快的建议变成了一个很长的答案。对不起。
正如泰勒在他的好回答中指出的那样,调用Dispose()
是一种很好的编程实践。这是因为这种方法应该“聚集在一起”所有需要的资源释放,因此没有不需要的开放资源。例如,如果您将一些文本写入文件,但未能关闭文件(释放资源),它将保持打开状态,并且在 GC 出现并执行您应该拥有的之前没有其他人能够写入它完成。
现在,在某些情况下,会出现更具体到您正在处理的类的“最终确定”方法,例如 StreamWriter.Close()
,它会覆盖 TextWriter.Close()
。实际上,它们通常更适合这种情况:例如,StreamWriter 的Close()
在对象的Dispose()
ing 之前刷新流和底层编码器!酷!
但是,浏览 MSDN,您会发现即使是 Microsoft 有时也会被众多的关闭器和处理器所迷惑。例如,In this webpage,在某些示例中,Close()
在隐含的 Dispose()
之前被调用(如果您不明白为什么它是隐含的,请参阅 using statement),尤其是他们不会费心去做。为什么会这样?我也很困惑。
我认为的原因(我强调,这是original research,如果我错了我肯定会失去声誉)是Close()
可能会失败,在保持资源打开的同时产生异常,而Dispose()
肯定会释放他们。这就是为什么Dispose()
应该始终保护Close()
呼叫(对不起,双关语)。
MyResource r = new MyResource();
try
r.Write(new Whatever());
r.Close()
finally
r.Dispose();
是的,我猜微软在那个例子上犯了错误。也许该时间戳永远不会刷新到文件中。
我明天要修复我的旧代码。
编辑:抱歉 Brannon,我无法评论你的回答,但你确定在 finally
块上调用 Close()
是个好主意吗?我猜一个例外可能会破坏块的其余部分,其中可能包含重要的清理代码。
回复 Brannon 的:太好了,只是不要忘记在真正需要时调用 Close()
(例如,在处理流时 - 不太了解 .NET 中的 SQL 连接)。
【讨论】:
实际上,我从不调用 Close(),我只是让 Dispose() 和“使用”构造做正确的事。如果您没有调用 Dispose,那么您需要以异常安全的方式调用 Close。在 finally 块中添加异常处理可能是个好主意。 对,我的 cmets 专门用于 SqlClient。关键是,您需要了解您正在使用的类。总是调用 Dispose 不一定是正确的答案。【参考方案6】:类型转换为 iDisposable,然后调用 dispose。这将调用配置为实现“iDisposable.Dispose”的任何方法,而不管函数的名称是什么。
【讨论】:
“函数名为”“Dispose”:所以我们回到最初的问题: 函数绑定到IDisposable.Dispose
,但这并不意味着就是这个名字。请注意,在 vb.net 中,可以将一个函数绑定到多个接口成员,其名称不必与函数的名称相关。
这样投:using (myObj as IDisposable)
【参考方案7】:
通常我们在 Close()、Abort() 和 Dispose() 中遇到问题,但让我告诉你它们之间的区别。
1) ABORT:- 我不建议使用它,因为当调用 abort 时,客户端会在不通知服务器的情况下删除连接,因此服务器会等待一段时间(大约 1 分钟)。如果您有批量请求,则不能使用 abort(),因为它可能会导致有限的连接池超时。
2) 关闭:- 关闭是关闭连接的好方法,因为当关闭连接时,它会调用服务器并确认服务器也关闭。
在这里,还有一件事要看。 在某些情况下,如果产生错误,那么在最后的 connection.close() 中编写代码不是一个好方法,因为那时通信状态将出错。
3) Dispose :- 这是一种关闭方式,但关闭连接后,您无法再次打开它。
所以试试这个方法,
private void CloseConnection(Client client)
if (client != null && client.State == CommunicationState.Opened)
client.Close();
else
client.Abort();
【讨论】:
对client != null
的检查不正确/具有误导性,因为它不能保护所有用法。另外,我不确定代码如何进入“此连接未打开,应该关闭”的状态。以上是关于Close and Dispose - 调用哪个?的主要内容,如果未能解决你的问题,请参考以下文章
更改“ReproClass”上的 Dispose 方法以在此字段上调用 Dispose 或 Close
我应该为流对象调用 Close() 或 Dispose() 吗?
Stream.Dispose 是不是总是调用 Stream.Close(和 Stream.Flush)