.NET - 用单个using语句替换嵌套的using语句

6
如果你遇到像这样嵌套使用语句/资源的C#代码:
using (var response = (HttpWebResponse)request.GetResponse())
{
    using (var responseStream = response.GetResponseStream())
    {
        using (var reader = new BinaryReader(responseStream))
        {
            // do something with reader
        }
    }
}

这样替换是否安全?
using (var reader = new BinaryReader(((HttpWebResponse)request.GetResponse()).GetResponseStream()))
{
    // do something with reader
}

上面的例子只是嵌套可释放资源的一个示例,如果使用不正确请谅解。我很好奇当您处理最外层的资源(在本例中为BinaryReader)时,它是否会递归地为您处理其子级,还是您需要使用单独的using语句显式地处理每个“层”?例如,如果您处理BinaryReader,它是否应该处理响应流,从而处理响应?考虑到上述最后一句话,我认为您实际上确实需要单独的using语句,因为无法保证包装器对象将处理内部对象。这样对吗?
5个回答

13

你只需要堆叠使用语句 - 它会产生你所期望的效果:

using (var response = (HttpWebResponse)request.GetResponse())
using (var responseStream = response.GetResponseStream())
using (var reader = new BinaryReader(responseStream))
{
    // do something with reader
}

本质上来说,这是相同的代码,只是格式不同(仅删除了大括号)。 - Reed Copsey
这会如何改善事情,这实际上就是 OP 刚刚做的事情,只是省略了前两个 using 语句中可选的花括号。 - slugster
是的,这是相同的,但与作者想要替换原始内容的东西相比,这更容易阅读和维护。 - Ken Richards

12

你需要分别使用不同的语句。

在您的第二个示例中,只有BinaryReader会被处理,而用于构造它的对象则不会被处理。

为了看清楚原因,请查看using语句实际上是做什么的。它接受您的第二段代码,并执行等效于以下内容的操作:

{
    var reader = new BinaryReader(((HttpWebResponse)request.GetResponse()).GetResponseStream());
    try
    {
      // do something with reader
    }
    finally
    {
        if (reader != null)
            ((IDisposable)reader).Dispose();
    }
}

正如您所看到的,ResponseResponseStream上永远不会有Dispose()调用。


BinaryReader.Close 方法关闭基础流。由于 Stream.Close 最终会调用 Stream.Dispose,因此在关闭读取器后无需关闭流。请参阅 http://msdn.microsoft.com/en-us/library/system.net.httpwebresponse.aspx 和 http://msdn.microsoft.com/en-us/library/system.io.stream.close.aspx。 - Jim Mischel
1
@Reed:如果你关闭了流,就不需要处理HttpWebResponse。请参阅http://msdn.microsoft.com/en-us/library/system.net.httpwebresponse.aspx。 "您必须调用Stream.Close或HttpWebResponse.Close方法之一来关闭响应并释放连接以供重用。不必同时调用Stream.Close和HttpWebResponse.Close,但这样做也不会导致错误。" - Jim Mischel
@Reed:我同意通常情况下最好处理而不是不处理。然而,在这种特殊情况下,没有必要这样做。 - Jim Mischel
1
@Jim:是的-我拿这段代码作为问题的例子-在这种情况下,不需要处理所有对象-但总的来说,应该这样做。然而,对于任何正确编写的IDisposable,都可以说同样的话-您永远不应该必须处理它们,因为finalizer 应该处理它,但这仍然是一个好习惯。 - Reed Copsey
@Jim:感谢您提供有关这个特定案例的信息 ;) - Reed Copsey
显示剩余7条评论

3

顺便提一下,这里有另一种拼写您原始示例的方式,可能可以满足对嵌套的不满:

using (var response = (HttpWebResponse)request.GetResponse())
using (var responseStream = response.GetResponseStream())
using (var reader = new BinaryReader(responseStream))
{
    // do something with reader
}

读者是否处理流是读者本身的功能,而不是'using'。就我记得的行为来看,这通常是读者的行为——当读者本身关闭时,它们会占用流并将其处理掉。但是上面提供的形式应该是可以的。


2

谢谢你的回答...我猜想还有其他可能性。我想除非你知道每种类型的作用,否则在怀疑时最好关闭或处理所有内容。 - Andy White

0

我在其他地方发布了这个问题,但是通过逗号分隔声明似乎会将每个以这种方式分隔的语句视为新声明并处理掉它们。

using (IType1 a = new Type1(), b = new Type1()){}

然而,这意味着您的对象必须是相同类型的。您可以像这样调用它们

using (IDisposable a = new Type1(), b = new Type2()){}

但是当然,您只能访问IDisposable方法,而不需要转换对象,这有点愚蠢。因此,我认为您可以使用

using (var a = new Type1(), b = new Type2()){}

这似乎为您提供了正确类型的对象引用,使您可以访问分配类型的正确方法,并处理创建的两个对象。如果有人知道我错在哪里,请告诉我,因为这对我来说似乎有效?(我知道这个问题真的很老,但这是我在搜索答案时找到的所有内容)


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