【问题标题】:What happens if i return before the end of using statement? Will the dispose be called?如果我在 using 语句结束之前返回会发生什么?会调用 dispose 吗?
【发布时间】:2011-03-15 22:14:15
【问题描述】:

我有以下代码

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

dispose() 方法是在using 语句大括号} 的末尾调用的,对吗?由于我在using 语句结束之前return,所以MemoryStream 对象会被正确处理吗?这里发生了什么?

【问题讨论】:

  • Aaap... 我收回了。经过一些非常专注的搜索,我刚刚找到了一个几乎重复的内容:stackoverflow.com/questions/2641692/… 现在,问题的提出完全不同,但最终的问题几乎相同。我想我们毕竟可以认为这是一个骗局。

标签: c# .net dispose idisposable using-statement


【解决方案1】:

是的,Dispose 将被调用。只要执行离开using 块的范围,它就会被调用,无论它以什么方式离开块,无论是块执行结束、return 语句还是异常。

正如@Noldorin 正确指出的那样,在代码中使用using 块被编译成try/finallyDisposefinally 块中被调用。比如下面的代码:

using(MemoryStream ms = new MemoryStream())
{
     //code
     return 0;
}

实际上变成:

MemoryStream ms = new MemoryStream();
try
{
    // code
    return 0;
}
finally
{
    ms.Dispose();
}

所以,因为finally 保证在try 块完成执行后执行,所以不管它的执行路径如何,Dispose 保证被调用,无论如何。

有关详细信息,请参阅this MSDN article

附录:
只是要补充一点:因为保证会调用Dispose,所以确保Dispose 在实现IDisposable 时永远不会引发异常几乎总是一个好主意。不幸的是,当调用Dispose 时,核心库中的某些类确实 抛出——我在看着你,WCF 服务引用/客户端代理! -- 当这种情况发生时,如果在异常堆栈展开期间调用了Dispose,则可能很难追踪原始异常,因为原始异常被Dispose 调用生成的新异常所吞噬。这可能会令人沮丧。还是那令人沮丧的疯狂?两者之一。也许两者兼而有之。

【讨论】:

  • 我想你会发现它被有效地编译成一个 try-finally 块并在 finally 中调用Dispose,所以它有效地完成了finally 的实现,正如你所描述的。
  • @Noldorin:完全正确。尽管我想我可以明确说明这一点。即将编辑....
  • 另外请注意,在某些情况下,finally 块不能保证执行,例如使用 Environment.FailFast 以及如果发生 StackOverFlowException。
  • @C.McAtackney:也是一个好点。此外,IIRC,OutOfMemoryException;基本上,如果您因为严重的执行失败而无法捕获异常,则不会调用 Dispose。当然,在这种情况下,程序肯定会崩溃,以及分配给它的任何内存,所以在 99.9% 的情况下,这不是问题,除非你在 dispose 方法中执行诸如写入文件之类的古怪事情.除了灾难性的程序崩溃之外,就是这样。
  • 您不应该在 WCF 中使用 'using()' 语句 - 请参阅 this article 了解更多信息。这是我用于 WCF 代理的 sn-p:' WCFProxy variableName = null;尝试 { variableName = new WCFProxy(); // 这里的 TODO 代码 variableName.Proxy.Close();变量名.Dispose(); } catch (Exception) { if (variableName != null && variableName.Proxy != null) { variableName.Proxy.Abort(); } 扔; } '
【解决方案2】:

using 语句的行为与try ... finally 块完全相同,因此将始终在任何代码退出路径上执行。但是,我相信他们会遇到极少数情况下不会调用 finally 块的情况。我记得的一个例子是,如果前台线程在后台线程处于活动状态时退出:除了 GC 之外的所有线程都被暂停,这意味着 finally 块没有运行。

明显的编辑:除了让它们处理 IDisposable 对象的逻辑之外,它们的行为相同,d'oh。

奖励内容:它们可以堆叠(类型不同):

using (SqlConnection conn = new SqlConnection("string"))
using (SqlCommand comm = new SqlCommand("", conn))
{

}

还有逗号分隔(类型相同):

using (SqlCommand comm = new SqlCommand("", conn), 
       comm2 = new SqlCommand("", conn))
{

}

【讨论】:

  • 根据stackoverflow中的另一个答案stackoverflow.com/a/9396151/6142097,对于第二个示例(逗号分隔),不应指定第二个参数的类型。
  • @mihkov 好点,我自己试过了。不过,我并没有回溯到以前的语言版本,所以我不知道这是否总是如此,或者与 Roslyn 有细微的变化。
【解决方案3】:

您的 MemoryStream 对象将被正确处理,无需担心。

【讨论】:

    【解决方案4】:
    【解决方案5】:

    编译后查看反射器中的代码。您会发现编译器会重构代码以确保在流上调用 dispose。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-19
      相关资源
      最近更新 更多