3

我已经阅读并且相信我理解 C#using语句的作用(如果我错了,请纠正我):将 IDisposable 对象初始化为只读到有限范围(using块)。我知道您可以在using并且不限制范围之前进行初始化,但这里建议不要这样做:

http://msdn.microsoft.com/en-us/library/yh598w02.aspx

我并不总是关注哪些类是什么的子类。我不太确定从 IDisposable 继承了哪些类。我不仅好奇可以在using语句中使用哪些类,而且我的同事希望在一个using块中找到哪些类?哪些类应该在一个using块中?另外,不使用using块而不调用 Dispose 真的有什么问题吗?它只是关于记忆还是稳定性?

4

10 回答 10

16

严格来说,任何实现IDisposable并且其范围仅限于该功能的对象都应该在一个using块内。该IDisposable接口的存在是为了允许处理非托管资源(数据库连接、文件句柄、窗口句柄等)的类及时、确定地处理这些资源。

一般来说,在IDisposable类中使用对象的方式有以下三种:

  1. 该对象在单个方法调用的范围内被创建并且不再需要。这是很常见的,并且是using可以(并且应该)使用的时候。
  2. 对象由类创建(或传递给类),其生命周期超出了单个方法调用的范围,但不超出类的生命周期。例如,您的类创建一个Stream并需要在对象的生命周期内使用它。在这种情况下,您的类应该实现IDisposable自己并在调用您自己的Dispose方法时处置您拥有的对象。这方面的一个例子是System.IO.StreamWriter
  3. 对象被传递给类,但类并不“拥有”它。这意味着IDisposable对象的可用生命周期超出了单个方法调用的范围,并且可能超出了对象的生命周期。在这种情况下,必须由其他人负责调用Dispose

第一种情况是您将遇到的最常见的情况,这就是该using块存在的原因。它负责确保对象将被处置,即使在异常情况下也是如此。

一些例子:

  • 流类
  • 数据库连接/命令
  • 控件

没有实现的类的详尽列表IDisposable,因为该列表将相当大并且充满了您可能永远不会遇到的类。想想班级了什么;它是否打开某种需要关闭的连接或文件?一般来说,它是否获得了某种需要释放的资源?如果是这样,它可能会实现它。在基本层面上,如果编译器允许您将它包含在 中using,那么它会实现IDisposable.

至于不打电话的后果Dispose,不用考虑。调用 Dispose。诚然,防御标准是,如果你的类直接使用非托管资源,那么你应该定义一个终结器,在你的对象被收集并且有人未能调用它的情况下调用 dispose,但这不应该是设计选择。曾经,据我所知。

于 2010-07-07T15:47:15.443 回答
13

这与记忆无关。它与其他资源有关,例如文件句柄、数据库连接等。

基本上,如果一个类实现了IDisposable,那是一个信号,表明你应该在完成后处理它,因为它可能有非托管资源,留下来会很昂贵。(例如,您的连接池可能会用完连接,或者文件句柄将保持打开状态,从而阻止另一段代码再次打开同一文件)。

于 2010-07-07T15:32:37.977 回答
2

您应该始终调用Dispose任何实现 的类,IDisposable这最容易通过using块来完成。

这不仅仅是关于记忆。这也不仅仅是关于资源。这是关于正确性的。

StreamWriter是一个著名的例子。微软甚至开发了一个 MDA来捕捉程序员忘记调用的情况Dispose。这不仅仅是内存或资源:在StreamWriter示例中,正在写入的文件可能会被截断。

有一次我不得不追踪一个令人讨厌的错误(实际上是在我老板的代码中),其中数据库事务正在回滚......结果原因是Dispose没有被调用,所以它也试图提交进程退出时对磁盘有很多影响(进程退出期间终结器超时)。修复只是几个using街区。

第三个示例:Microsoft 的托管 ESENT 包装类具有“三层”处置方案,需要 Dispose以正确的顺序调用(“外部”类最后)。

因此,存在三个真实示例,如果调用不正确,将导致不正确的行为。Dispose其他类可能表现出类似的行为。

作为一般规则,您应该始终调用Dispose.

于 2010-07-07T15:46:05.173 回答
1

至少,所有使用非托管资源的类

于 2010-07-07T15:31:23.470 回答
1

不使用 using 块并且不调用 Dispose 绝对有很多错误,您很可能会泄漏内存和/或资源。使用是一种方便,但您确实应该在类派生自 IDisposable 的任何对象中调用 Dispose。

于 2010-07-07T15:32:46.277 回答
1

至于哪些类是一次性的,你可以自己探索智能感知,或者你只是从经验中学习。一些常见的包括Image及其子项,实际上是大多数System.Drawing命名空间、大量文件流、数据库连接等。

至于何时应该调用它 - 尽快。如果你知道它是一次性的,并且你知道你已经完成了它,然后调用 Dispose。

我相信许多IDisposable实现一次性模式的 .NET 基类都实现了,这意味着当垃圾收集器来获取它们时,它们将被正确地处理掉。但即便如此,你还是应该在完成后处理掉东西,因为

  1. 您可能会留下一个参考(泄漏)并且不会被处理,或者
  2. GC 可能暂时不会出现。

此外,对于您自己编写的类,垃圾收集并不等同于对非托管资源的正确处理——这是一个常见的混淆。一次性模式需要自己实现。

于 2010-07-07T15:37:20.317 回答
1

类实现的主要原因IDisposable是释放非托管资源。垃圾收集器将在托管资源超出范围并认为合适时释放它们,但它不知道非托管资源。调用 Dispose 方法将显式释放资源。

如果您不使用using块或调用该Dispose方法,那么您将遇到内存泄漏问题,进而可能导致稳定性问题。

using处理实现IDisposable. 尽管您可能更喜欢Try.. Catch.. Finally确保Disposefinally块中调用,以便处理异常。

于 2010-07-07T15:42:24.140 回答
1

IDisposable 的目的是使与非托管资源(如文件、数据库或图形上下文)交互的对象能够自行清理。using 语句是以下构造的便捷简写

var disposable = new MemoryStream();
try
{
  //do some work with the disposable object
}
finally
{
  if (disposable!=null)
     disposable.Dispose();  
}

问题当然是知道哪些对象实现了 IDisposable ......不幸的是,除了文档之外,没有自动化的方法可以知道。虽然我相信有一个 Fxcop 设置可以检查在使用之外使用的 IDisposables。

于 2010-07-07T15:54:16.890 回答
0

您不必担心在 using 块下使用哪些类。如果您在 using 语句下使用的未实现 IDisposbale,那么它将显示红/蓝波。这样您就可以知道它缺少一次性接口。据我使用的几乎所有类的框架都实现了 Idisposable。但是在自定义类中,您需要实现

于 2010-07-07T15:33:32.773 回答
0

可以调用的类的现成推算器using是:

  • 数据库连接、命令和数据读取器
  • 读者和作家

您还应该 Dispose 更多的类,但上面的那些是经常使用的,通常在狭窄的使用范围内。

是的 - 存在不使用using和不调用的问题dispose。经典是一种 Web 应用程序,它不会关闭与数据库的连接,在保持连接打开时有效地从数据库服务器中吸取资源。

于 2010-07-07T15:50:47.317 回答