【问题标题】:Are "using" statements "bad code"? [closed]“使用”语句是“坏代码”吗? [关闭]
【发布时间】:2012-03-26 19:10:21
【问题描述】:

我读过这样的用法:

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

可以这样想:

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

所以如果 myObejct.DoStuff 抛出 ExceptionA 然后 myObject.Dispose() 也抛出异常 (ExceptionB) 那么 ExceptionA 将丢失。 (请参阅MSDN examples here 以获得更好的描述。)

这是否意味着如果 using 块代码中的代码可能引发异常(这是大多数代码对吗?) 那么 using 语句是一种不好的做法?

【问题讨论】:

  • “可以这样想”有点轻描淡写。 :)
  • Dispose有什么业务抛出异常?
  • @blzm 不完全是那段代码……那是 using 语句创建的代码的简化版本。
  • 我个人更喜欢使用 try/catch 封装,以防 Dispose 抛出异常。但正如其他人已经提到的那样。如果 Dispose 一开始就抛出异常,那就真的有问题了。
  • @Vaccano:我不是说Dispose 不能抛出异常,我说它没有业务这样做。那些物体被打破了。幸运的是,Marc 展示了一种使用包装对象修复它们的方法。

标签: c# exception-handling using-statement


【解决方案1】:

这是否意味着如果 using 块代码中的代码可能引发异常(这是大多数代码对吗?)那么 using 语句是一种不好的做法?

没有。

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

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

这是这里的“坏习惯”。 IDisposable.Dispose 实现应该真正设计为不会引发异常,除非在真正不可恢复的情况下。

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

话虽如此,我不认为 using 语句本身是一种不好的做法。事实上,这往往是一种非常好的做法,因为它避免了一个非常常见的陷阱(在异常情况下错过了资源的可处置性)。

【讨论】:

  • +1,从Dispose 调用中抛出相当于从 C++ 析构函数中抛出。当您认为集合可破坏项目时,调用代码不可能正确
【解决方案2】:

using 语句确保实现 IDisposable 的类型被正确处理(也就是说,syntactic sugar 用于正确实现 Dispose pattern)。

它们是非常良好做法

有一个 Dispose 函数抛出 不好的做法。

【讨论】:

  • 对问题中的异常有特别关注。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

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

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

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-01-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-30
      • 2014-01-02
      • 1970-01-01
      相关资源
      最近更新 更多