关闭并处置-呼叫哪个?


Answers:


190

我想澄清这种情况。

根据Microsoft的准则,Close在适当的地方提供方法是一种很好的做法。是《框架设计指南》的引文

如果附近是该地区的标准术语,请考虑提供方法Close(),除了Dispose()。这样做时,使Close实现与Dispose... 相同非常重要。

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

一个应用程序可以调用Close多次。没有异常产生。

如果调用Dispose方法, SqlConnection对象状态将被重置。如果尝试在已处置SqlConnection 对象上调用任何方法,则会收到异常。

说:

  • 如果一次使用连接对象,请使用Dispose
  • 如果必须重用连接对象,请使用Close方法。

5
@ Chris,Close()的文档说:“然后释放到连接池的连接,如果禁用了连接池,则关闭连接。” 因此Close()应该足以防止连接池溢出。
大卫·哈蒙德

@DavidHammond:你是对的。我要删除以前的评论。
NotMe 2011年

3
.Dispose()是否还将连接释放回池中?
oscreatingcretin 2012年

这是十年来我读过的关于该主题的最佳论据。好点。
Michael Erickson

1
因此它以这种方式工作1. con.Open() con.Close(); 2 con.Open(); // reuse 3. con.Dispose(); // use one time con.Open(); // error
shaijut

24

像往常一样,答案是:取决于情况。不同的类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块中)进行操作。


当您说“由您决定进行必要的研究”时,该研究是什么?我知道如何确定地说的唯一方法是通过反思,但这在大多数情况下具有“非法”的缺点。
风暴

6
我不会说:conn.Close(); // Optional这不是可选的。这是多余且不必要的。您将对象处置两次,某些代码分析工具会将其标记为警告。
Metalogic

@Metalogic我同意使用适当的用法来调用Close是不必要的(而且很丑)。但是,挑剔:调用Close 并不是 “在处理”(而Dispose表示对SqlConnection使用Close)。与相比using (var x = ..) { x.Dispose(); },在这种情况下x确实是“配置两次”。
user2864740

11

您确实需要调用Dispose()!

Dispose()供开发人员调用,垃圾收集器则调用Finalize()。如果您不对对象调用Dispose(),则直到垃圾回收器出现并对其进行调用最终确定(谁知道那将在何时发生)之前,它们所使用的任何非托管资源都不会被处理。

这种情况称为非确定性终结,并且是.net开发人员的常见陷阱。如果您正在使用实现IDisposable的对象,请对它们调用Dispose()!

http://www.ondotnet.com/pub/a/oreilly/dotnet/news/programmingCsharp_0801.html?page=last

虽然可能有很多实例(例如在SqlConnection上),您在某个对象上调用Disponse(),而它只是在其连接上调用Close()或关闭文件句柄,但调用Dispose()几乎总是您的最佳选择!除非您计划在不久的将来重用该对象。


26
这个评论是完全错误的。垃圾收集器永远不会调用Dispose
史蒂芬·克利西

3
结论:如果不与实现的类一起使用,则应调用。如果被调用的类实现了IDisposable,并且您已将其用法包装在的页面中,那么您可以使用(双关语,所以射击我)进行处理。使用,但建议使用任何明确的利用,据我所知。Dispose() using()IDisposableusing()Dispose()Close()Open()
勒内Kåbis

我不确定其他DBMS,但您不能在PostgreSql中都做到这一点。一旦你Close连接,Postgres自动设置连接标识符null。从那里开始,不能Dispose使用已经设置为的sql连接标识符null
ssd

10

对于SqlConnection,从连接本身的角度来看,它们是等效的。根据Reflector的说法,Dispose()调用Close()以及执行一些其他的释放内存的操作-主要是将成员设置为等于null。

对于Stream,它们实际上是等效的。 Stream.Dispose()只需调用Close()。


1
你确定吗?MSDN表示它是从Component那里继承而来的它似乎没有做任何尝试Close()。我看不到任何地方DBConnectionSqlConnection与这些通知有关的任何地方。但是,它确实有一个私有资源DisposeMe(),在任何地方都没有引用
Deanna

@Deanna正是在这里overrided:github.com/dotnet/corefx/blob/...
大卫Cumps

@DavidCumps看来,自从我写那条评论以来的四年中,它已经发生了变化。我的链接不再有效。
Deanna'1


6

这可能很快就会成为一个很长的答案。抱歉。

正如tyler在他的好答案中指出的那样,调用Dispose()是一种很棒的编程习惯。这是因为该方法应该“聚集在一起”释放所有需要的资源,因此不需要多余的开放资源。例如,如果您向文件中写入了一些文本,但未能关闭文件(释放资源),则该文件将保持打开状态,直到GC出现并执行您应做的一切之前,其他任何人都无法写入该文件。完成。

现在,在某些情况下,将针对您正在处理的类(例如StreamWriter.Close())覆盖更具体的“完成”方法TextWriter.Close()。确实,它们通常更适合这种情况:Close()例如,StreamWriter的StreamWriter Dispose()在对象被放入之前先刷新流和基础编码器!凉!

但是,浏览MSDN时,您会发现,即使是Microsoft,有时也会因大量的关闭器和处置器而感到困惑。例如,在此网页中,在某些示例Close()中,在隐式代码之前调用Dispose()(如果您不理解为什么它是隐式的,请参阅using语句),尤其是它们不会打扰。为什么会这样呢?我也很困惑。

我认为(并且我强调,这是原始研究,如果我错了,我肯定会失去声誉)的原因是,它Close()可能会失败,产生一个例外,同时保持资源开放,同时Dispose()肯定会释放它们。这就是为什么a Dispose()应该始终维护Close()呼叫的原因(对双关语很抱歉)。

MyResource r = new MyResource();

try {
  r.Write(new Whatever());

  r.Close()
finally {
  r.Dispose();
}

是的,我想微软会漏掉一个例子。也许该时间戳永远不会刷新到文件中。

我明天要修正旧代码。

编辑:对不起布兰农,我不能对你的解答发表评论,但你确定这是一个好主意,调用Close()一个上finally块?我猜一个例外可能会破坏其余的块,其中可能包含重要的清理代码。

回答Brannon的问题:太好了,只是别忘了Close()在确实需要时调用它(例如,在处理流时-对.NET中的SQL连接了解不多)。


实际上,我从不调用Close(),我只是让Dispose()和“ using”构造做正确的事情。如果不调用Dispose,则需要以异常安全的方式调用Close。将异常处理添加到finally块可能是一个好主意。
布兰农

是的,我的评论专门针对SqlClient。关键是,您需要了解所使用的类。始终致电Dispose不一定是正确的答案。
布兰农

2

将类型转换为iDisposable,然后对其进行调用处理。这将调用配置为实现“ iDisposable.Dispose”的任何方法,而与函数的名称无关。


“函数被命名为” Dispose”:所以我们回到最初的问题:}
user2864740 '18

该函数绑定到IDisposable.Dispose,但这并不意味着它就是名称。请注意,在vb.net中,可以将一个函数绑定到多个接口成员,而这些接口成员的名称不必与该函数的名称相关。
超级猫

像这样投射:using (myObj as IDisposable)
Yousha Aleayoub '19

2

通常,我们在Close(),Abort()和Dispose()中面临问题,但让我告诉您它们之间的区别。

1)ABORT:-我不建议使用此方法,因为当调用abort时,客户端将删除连接而不通知服务器,因此服务器将等待一段时间(大约1分钟)。如果您有批量请求,则不能使用abort(),因为它可能会导致有限的连接池超时。

2)关闭:-关闭是关闭连接的好方法,因为关闭连接时,它将调用服务器并确认服务器也在该侧关闭。

在这里,还有另一件事要看。在某些情况下,如果产生错误,则不是在该connection.close()中最终编写代码的好方法,因为那时通讯状态将出错。

3)处置:-这是一种关闭方式,但是在关闭连接后无法再次打开它。

因此,尝试这种方式,

private void CloseConnection(Client client)
    {
        if (client != null && client.State == CommunicationState.Opened)
        {
            client.Close();
        }
        else
        {
            client.Abort();
        }
    }

该检查client != null不正确/具有误导性,因为它不能保护所有用法。另外,我不确定代码如何达到“此连接未打开且应关闭”的状态。
user2864740
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.