是"使用"陈述"坏代码"?

时间:2012-03-26 19:10:22

标签: c# exception-handling using-statement

我已经读过这样的使用:

using (myObject)
{
   myObject.DoStuff();
}

可以这样想:

try
{
   myObject.DoStuff();
}
finally
{
   myobject.Dispose()
}

因此,如果myObejct.DoStuff抛出ExceptionA,然后myObject.Dispose()也抛出异常(ExceptionB),则ExceptionA将丢失。 (有关更好的说明,请参阅MSDN examples here。)

这是否意味着如果使用块代码中的代码可能抛出异常(大多数代码是正确的?)那么using语句是不好的做法?

4 个答案:

答案 0 :(得分:18)

  

这是否意味着如果使用块代码中的代码可能抛出异常(大多数代码是正确的?)那么使用using语句是不好的做法?

没有

  

然后myObject.Dispose()也抛出异常

这确实是你问题的症结所在。

这是“坏习惯”。 IDisposable.Dispose实现应该真正设计,以便它们不会引发异常,除非在真正无法恢复的情况下。

由于IDisposable确实是为了释放有问题的资源,因此主要问题应该是确保在大多数情况下不会抛出此实现。使用清理方法抛出会引起很多悲伤 - 这也是using statement shouldn't be used with WCF clients等等的原因。

话虽如此,我认为使用声明本身并不是一种坏习惯。事实上,它往往是一种非常好的做法,因为它避免了一个非常常见的陷阱(在例外情况下缺少一次性资源)。

答案 1 :(得分:6)

using语句可确保正确处理实现IDisposable的类型(即syntactic sugar正确实现Dispose pattern)。

他们非常良好做法

使用 Dispose 函数抛出不好的做法。

答案 2 :(得分:6)

这是WCF中的常见问题,Dispose()经常抛出异常。有一种方法可以包装一次性使您可以继续获得using()语句的好处,而不会有丢失异常的风险。它基本上吞噬了Dispose期间抛出的任何异常,因此原始异常总是被抛出到更高的上下文中。

http://marcgravell.blogspot.com/2008/11/dontdontuse-using.html

答案 3 :(得分:2)

.net中异常处理的一个基本限制是,由于它是通过C ++异常处理模型构建的,因此所有与当前异常上下文相关的信息都必须封装在一个异常对象中;实际上,在C#中,所有与是否应该捕获异常相关的信息都必须封装在一个异常对象的类型中。此外,在C#中,发现异常的唯一方法就是同意捕获它,并且没有办法以声明的方式表明一个人希望对异常采取行动,但无意对其进行充分处理。被视为“已解决”。所引用的“使用”困难源于这些限制。

在现实世界中,Dispose()期间发生的事情可能会破坏任何不期望它们的代码流。这种情况通常应该是例外。不幸的是,如果Dispose正在运行,因为发生了一些其他异常,并且在其中发生异常,那么在C#中只有三个实际操作过程:

  1. 丢失导致`Dispose`运行的异常的所有信息,因为'Dispose`或调用它的代码无法获取该信息,并且`finally`块中发生的任何异常都会破坏任何有关任何先前待定指令的信息。
  2. 扼杀在`Dispose`中发生的异常 - 可以将其记录在某个地方,但除非调用者明确地检查日志,否则它将不知道发生了异常。
  3. 捕获任何和所有类型的异常,将其类型捕获到变量,然后重新抛出;在`finally`块中对有问题的变量进行操作。

在vb.net中,第四种可能性具有更好的语义,但需要看起来很奇怪的代码:Exception并使用异常过滤器来锁定,而不会捕获变量发生的任何异常,如同上面的#3,在“finally”块中对该变量进行操作。

“using”语句提供上面列出的第一个语义。在某些情况下,其他方法可能会更好。我希望vb和C#提供一个“finally”语句的版本,该语句接受Exception类型的参数。它可以允许上面的#4语义(在语义上是最好的)没有丑陋的代码。