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 相同 ...

在大多数情况下,CloseDispose 方法是等效的。在SqlConnectionObject 的情况下,CloseDispose 之间的主要区别是:

应用程序可以调用Close更多 不止一次。没有例外是 生成。

如果你调用了Dispose 方法 SqlConnection 对象状态将是 重置。如果您尝试调用任何 处理方法SqlConnection 对象,您将收到异常。

也就是说:

如果使用连接对象一 时间,使用Disposeusing 块将确保即使在发生异常时也会调用它。 如果必须重用连接对象, 使用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 from Component 哪个doesn't seem to do anything to try and call Close()。我在DBConnectionSqlConnection 中看不到与这些通知中的任何一个相关的任何地方。但是它确实有一个私人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)

深入解析Close()和Dispose()的区别

我需要 Dispose() 或 Close() EventWaitHandle 吗?

SQL中的close和dispose有啥区别? [复制]