在同一个 using 块内使用多个变量

5

我目前正在使用以下两个对象:

using (var ms = new MemoryStream())
using (var bw = new BinaryWriter(ms))
{
    // work with ms and bw, both referenced here
}

它工作“良好”,实际上在这里也有一个答案。然而,当我运行VS2012的代码分析工具时,我会收到一个警告:

CA2202  Do not dispose objects multiple times   
Object 'ms' can be disposed more than once in method '<my method name>'. 
To avoid generating a System.ObjectDisposedException you should not 
call Dispose more than one time on an object.

这让我相信可能有一种替代方法来处理这种情况,但我不知道是什么。
有人知道在单个using块中以无警告的方式使用两个对象的“适当”方法是什么吗?

@HansPassant:谢谢,但我不一定会称它为重复,因为那个针对的是VB.net的语法(尽管事后你可以看到相似性源自共同的IL)。 - DeepSpace101
代码分析器不关心你使用的编程语言。 - Hans Passant
4个回答

2

BinaryWriter类是这样编写的,当您在dispose BinaryWriter时,它会处理您传递给它的底层流,除非您使用可选的构造函数参数来告诉它不要这样做。由于二进制写入器正在处理底层内存流,并且您还在using块中处理它,因此它被处理了两次。

现在,对于大多数类来说,这并不是真正的问题,因为它们应该(并且内存流也应该)被编写成可以在被处理两次的情况下正常工作,只要您在处理后不再使用它(您没有),因此您可以安全地忽略警告。如果您想让警告消失,可以将内存流从using中取出,因为它已经被处理了。


1
如果您不需要对 MemoryStream 的对象引用,那么这将很好地工作。
using (var bw = new BinaryWriter(new MemoryStream()))
{
    var memStream = bw.BaseStream as MemoryStream;
}

否则,请使用多个using语句。特别是在使用.NET框架对象的情况下,因为它们实现了正确的释放模式。
当对象未正确实现释放模式时,双重using语句可能会导致问题。
PS:您是否以这种方式收到相同的警告:
using (var memStream = new MemoryStream())
{
    using (var bw = new BinaryWriter(memStream))
    {
        // work with ms and bw
    }
}

我喜欢那个想法,但是MemoryStream在代码块中被引用(ms.ToArray())。 - DeepSpace101
你仍然可以通过 bw.BaseStream 访问你的 MemoryStream。但说实话,我会选择嵌套的 using - bas

1
你的假设是这是一个单一的using块是不正确的。 你示例中的代码是语法糖,实际上是一个嵌套的using块,像这样:
using (var ms = new MemoryStream())
{
   using (var bw = new BinaryWriter(ms)) 
   {
    // work with ms and bw, both referenced here
   }
}

没有一种单一的方法可以正确地书写并避免警告。在内部资源包装外部资源且它们同时超出作用域的情况下,您需要查阅文档:如果合同规定内部资源的处理方法调用了包装对象的处理方法,则您根本不需要将外部资源放入using块中。
话虽如此,在大多数文档中可能不会提到这些细节。您可以通过自己检查行为(通过继承和观察副作用)来检查,但是您必须小心,因为首先没有记录这种行为可能会在未来的版本中更改。
因此,如果您非常担心,可以保留双重using块(以防万一),抑制警告,并将整个块放入try {} catch(ObjectDisposedException e){} 中,以完全保护自己。
听起来有点偏执?这取决于您正在使用哪个库 :-)

二进制写入的文档确实指定了它会处理包装资源,直到你使用指示不这样做的构造函数重载。 - Servy

-4

我认为你不应该在以下情况下使用 "using( ... ){}":

using (var ms = new MemoryStream())

因为操作系统管理内存并对其进行“处理”!


释放内存流是一个好的编程实践。 - bas
这个答案非常误导人。Dispose方法可以被终结器自动调用,但是(a)这与操作系统无关,它是垃圾回收机制的一部分,(b)您应该确定性地处理非托管资源。 - Vitaliy

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