我需要在每个实现IDisposable接口的对象中使用“using”关键字吗?

5

我正在调用一个第三方库,其中有很多类都实现了IDisposable接口。

我是否需要在所有这些类上使用using模式?


在对象超出作用域之前,应该对你“拥有”的对象(使用new或工厂方法创建的对象)调用Dispose。使用using块是实现这一点的简单方法。 - Rich O'Kelly
这是一个理想的模式。如果你按照这种方式做,你可以确保所有未由你控制的底层资源都被正确释放。为什么不呢? - Matías Fidemraizer
不是必须的,但如果你不这样做,那么你需要确保处理它们以及它们拥有的所有资源。 - ChrisBD
这是一个非常好的问题,只有当我成为更高级的.NET开发人员时,才理解了答案。你会认为像这样的东西会更明确地定义,但实际上并不是这样。 - Zenexer
我不知道为什么有人把我的答案转换成评论。 - Matías Fidemraizer
4个回答

5

你不必须这么做,但这是一个好的实践。

它确保资源被正确清理,无论是否发生异常。

IDisposable只应该在需要清理资源的类上实现,因此确保它们这样做是一个好的实践。

可能会有一些情况需要直接调用Dispose而不是使用块(WCF代理就是臭名昭著的例子),但这并不是常规情况。

简而言之 - 没有人会强制你使用它们,但你真的真的应该这样做。


@MatíasFidemraizer - 我只是在说,没有“using”警察来强制执行对“IDisposable”的使用。也许我错了,在西班牙有这样的警察;) - Oded
@Odded 当然,西班牙有它!;) 没有警察强制执行这个,但为什么要冒这个风险呢?或者为什么要使用try/catch/finally,如果有一个好的语法糖呢?:D - Matías Fidemraizer
抱歉,@Oded,我做了两次。对不起。 - Matías Fidemraizer
我可以建议另外加上一条注释,即有些情况下您可能希望显式调用Dispose()而不是使用using块吗? - Andrew Barber
因为我在移动设备上,很难正确地格式化代码项。此外,我可能会冗长,而你的答案则以“简而言之”结束:D - Andrew Barber
显示剩余5条评论

3

这要根据变量的作用域来决定。

本地作用域:使用

如果该变量具有本地作用域,是的,应该在相关代码中使用 using 块。请记住,using 块只是语法糖,假设 using 封装了一个名为 obj 的 IDisposable 对象,则相当于以下代码:

var obj = // ...
try
{
    // ...
}
finally
{
    obj.Dispose();
}

这意味着,即使出现异常,您的对象也将被处理。

类作用域:IDisposable

如果��的对象作用域在类级别,则不应将其包含在using块中。相反,您的类应通过实现IDisposable来向使用它的任何代码公开Dispose方法,并在那里处理该对象。

永远不要使用:Finalize

通常情况下,依靠类的finalizer来释放其对象的处理责任,会破坏此依赖关系链中的任何点,并不是好的做法。这会削弱DisposeFinalize之间的区别: Dispose用于显式、立即释放资源,而Finalize则更为被动。但是,这更多是我个人的编程风格和观点,不要把它当作事实。您应该自行进行更多的研究,特别是阅读相关评论,然后再采纳我的建议。我肯定错过了重要的例外,至少是其中之一。


1
你提到了其他许多人似乎忽略了的一点:有时候你想显式地调用Dispose()而不是使用using块。 - Andrew Barber
在你们都会说“Finalize,哦,那是Java而不是C#”之前,.NET中的析构函数通常被称为Finalize - Zenexer
@AndrewBarber 是的,这就是为什么即使有很多其他答案已经发布,我仍然继续发布我的答案。有时候 -- 很多时候 -- 明确调用 Dispose 更好。对于类级别的变量,使用 using 没有意义。 - Zenexer
1
@Zenexer:这不仅仅是“通常用Finalize命名”。如果一个类覆盖了 Object.Finalize(),那么对象在该方法被调用或使用 GC.SuppressFinalize() 指示它不必要之前,将不会被垃圾回收。由于几乎每个使用 C# 析构函数的程序都需要正确(析构函数是一种语法单元,请求编译器覆盖 Object.Finalize)包括调用GC.SuppressFinalize()方法,因此“finalize”这个术语似乎比“destructor”更合适,尽管后者术语仍然需要描述C#包装器。 - supercat
@supercat 我知道使用Finalize有很多合适的理由(这也是我使用它的原因),但你会惊讶地发现有多少人听到它就会尖叫“Java!”。不过,你的解释非常好。 - Zenexer

3

你“不一定需要”这样做,但最好这样做。

如果你不这样做,你可能会耗尽一些资源,或者根据该库的具体作用,甚至会得到错误的结果。

但是如果不知道Dispose()对这些对象具体执行了什么操作,为避免出现意外问题,确实应该调用它。(而且你不必直接调用,可以使用using来处理,就像你建议的那样。)


0
当一个类实现IDisposable接口时,它意味着它希望被您的代码处理而不是等待垃圾收集器在以后的某个时间处理它。所以是的,如果一个类实现了IDisposable接口,则在它超出范围之前应调用Dispose方法(或使用using)。

你应该使用using而不是直接调用Dispose吗?同样是的。为确保对象被处理,即使抛出异常,您也必须确保您的对象已经被处理,通过将您的代码封装在try {...} finally {}块中,在finally块中处理您的对象。这会导致不必要的混乱,并且您可能会轻易地忘记添加finally块。

使用using更加安全可靠。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接