如果我在using语句结束之前返回,该怎么办?请问该处置?


115

我有以下代码

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

dispose()方法在using语句括号的末尾调用}对吗?自从我 returnusing声明末尾之前,将MemoryStream正确处置该对象吗?这里会发生什么?


4
@JonH:找到确切的副本,然后在这种情况下投票关闭。
Noldorin

@Noldorin:我去找一个骗子,因为我想一定是有人问过这个,但是我找不到。我想那里仍然有一些简单的问题。:)
Randolpho

@JonH和@Noldorin-在问题形成时会出现重复的内容,它会搜索“相似的问题”,人们似乎对此没有足够的使用。
亚当·霍兹沃思

@Adam:自己去尝试。复制/粘贴标题,并查看系统显示了哪些重复项。我会给你一个提示:答案是否定的。同上,如果您通过Google或SO的搜索进行搜索。看来以前没有问过这个问题。
Randolpho

Aaap ...我收回了。经过一些非常专门的搜索后,我发现几乎是重复的:stackoverflow.com/questions/2641692/…现在,问题的提出方式完全不同,但最终的问题几乎相同。我想我们毕竟可以认为这是骗子。
Randolpho

Answers:


167

是的,Dispose将被呼叫。一旦执行离开了using块的范围,就会立即调用它,无论离开块是用什么方式进行的,无论是块执行的结束,return语句还是异常。

正如@Noldorin正确指出的那样,using在代码中使用代码块被编译为try/ finally,并在代码块中Dispose被调用finally。例如下面的代码:

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

有效地变为:

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

因此,由于finally保证了在try块完成执行之后执行,所以无论其执行路径如何,Dispose都可以确保调用它,无论如何。

有关更多信息,请参见MSDN文章

附录:
需要注意的一点Dispose是:由于可以被调用,因此Dispose在实现时确保不抛出异常几乎总是一个好主意IDisposable。不幸的是,有一些类核心库在某些情况下抛出时Dispose叫-我看着你,WCF服务参考/客户端代理!-并且发生这种情况时,如果Dispose在异常堆栈展开期间调用了原始异常,则可能非常困难,因为原始异常会被吞咽而偏向于Dispose调用生成的新异常。这可能令人发疯。还是那令人沮丧的发疯?两者之一。也许两者都有。


4
我认为您会发现它已经被有效地编译为try-finally块,Dispose并最终调用了in,因此它有效地解决了finally您所描述的的实现。
Noldorin

@Noldorin:完全是。尽管我想我可以对此明确。即将编辑...。–
Randolpho

1
还要注意,在某些情况下不能保证执行finally块,例如使用Environment.FailFast以及是否发生StackOverFlowException。
Christopher McAtackney,2010年

@ C.McAtackney:也是一个好点。另外,IIRC,OutOfMemoryException;基本上,如果因为严重执行失败而无法捕获异常,则不会调用Dispose。当然,在这种情况下,保证程序以及分配给它的所有内存都将崩溃,因此在99.9%的情况下,它不是问题,除非您在执行诸如在dispose方法中写入文件之类的怪事。除了灾难性的程序崩溃之外,那就是。
Randolpho

您永远不要在WCF中使用'using()'语句- 有关更多信息,请参阅本文。这是我用于WCF代理的代码段:'WCFProxy variableName = null; 尝试{variableName = new WCFProxy(); //这里的TODO代码variableName.Proxy.Close(); variableName.Dispose(); } catch(Exception){if(variableName!= null && variableName.Proxy!= null){variableName.Proxy.Abort(); } throw; }'
Dave Black

18

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), 
       SqlCommand comm2 = new SqlCommand("", conn))
{

}



0

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.