此代码的正确 IDisposable 实现
我有以下代码
public static byte[] Compress(byte[] CompressMe)
{
using (MemoryStream ms = new MemoryStream())
{
using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress,true))
{
gz.Write(CompressMe, 0, CompressMe.Length);
ms.Position = 0;
byte[] Result = new byte[ms.Length];
ms.Read(Result, 0, (int)ms.Length);
return Result;
}
}
}
这工作正常,但是当我对其运行代码分析时,它会显示以下消息
CA2202 : Microsoft.Usage : Object 'ms' can be disposed more than once in
method 'Compression.Compress(byte[])'. To avoid generating a
System.ObjectDisposedException you should not call Dispose more than one
time on an object.
就我而言,当 GZipStream 被处置时,它会使底层流(毫秒)保持打开状态,因为到构造函数的最后一个参数(leaveOpen=true)。
如果我稍微改变我的代码..删除 MemoryStream 周围的“using”块并将“leaveOpen”参数更改为 false..
public static byte[] Compress(byte[] CompressMe)
{
MemoryStream ms = new MemoryStream();
using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress, false))
{
gz.Write(CompressMe, 0, CompressMe.Length);
ms.Position = 0;
byte[] Result = new byte[ms.Length];
ms.Read(Result, 0, (int)ms.Length);
return Result;
}
}
然后就会出现..
CA2000 : Microsoft.Reliability : In method 'Compression.Compress(byte[])',
object 'ms' is not disposed along all exception paths. Call
System.IDisposable.Dispose on object 'ms' before all references to
it are out of scope.
我无法获胜..(除非我错过了一些明显的东西)我尝试过各种事情,比如在块周围放置一个 try/finally ,以及在那里处置 MemoryStream,但它要么说我要处置它两次,要么根本不处置!
I have the following code
public static byte[] Compress(byte[] CompressMe)
{
using (MemoryStream ms = new MemoryStream())
{
using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress,true))
{
gz.Write(CompressMe, 0, CompressMe.Length);
ms.Position = 0;
byte[] Result = new byte[ms.Length];
ms.Read(Result, 0, (int)ms.Length);
return Result;
}
}
}
This works fine, but when I run code analysis on it, it comes up with the following message
CA2202 : Microsoft.Usage : Object 'ms' can be disposed more than once in
method 'Compression.Compress(byte[])'. To avoid generating a
System.ObjectDisposedException you should not call Dispose more than one
time on an object.
As far as I'm concerned, when the GZipStream is Disposed, it leaves the underlying Stream (ms) open, due to the last parameter of the constructor (leaveOpen=true).
If I change my code slightly.. remove the 'using' block around the MemoryStream and change the 'leaveOpen' parameter to false..
public static byte[] Compress(byte[] CompressMe)
{
MemoryStream ms = new MemoryStream();
using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress, false))
{
gz.Write(CompressMe, 0, CompressMe.Length);
ms.Position = 0;
byte[] Result = new byte[ms.Length];
ms.Read(Result, 0, (int)ms.Length);
return Result;
}
}
This then comes up with..
CA2000 : Microsoft.Reliability : In method 'Compression.Compress(byte[])',
object 'ms' is not disposed along all exception paths. Call
System.IDisposable.Dispose on object 'ms' before all references to
it are out of scope.
I can't win.. (unless I'm missing something obvious) I've tried various things, like putting a try/finally around the block, and Disposing of the MemoryStream in there, but it either says that I'm disposing of it twice, or not at all !!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
除了处理问题之外,您的代码也被破坏了。您应该在读回数据之前关闭 zip 流。
另外,
MemoryStream
上已经有一个ToArray()
方法,无需自己实现。我只是抑制警告,因为它是误报。代码分析是为您服务的,而不是相反。
Apart from the disposing issue, your code is also broken. You should close the zip stream before reading back the data.
Also there is already a
ToArray()
method onMemoryStream
, no need to implement that yourself.I'd simply suppress the warning, since it's a false positive. Code analysis is there to serve you, not the other way round.
来自 MSDN 中的此页面
这是一次尝试...终于您的解决方案中缺少导致第二条消息的信息。
如果:
失败,则不会处理该流。
From this page in the MSDN
It is the try...finally that is missing from your solution that causes the second message.
If this:
fails the stream will not be disposed.
实际上,在内存流上有效地调用两次 dispose 不会导致任何问题,在 MemoryStream 类中以及在 Microsoft 的测试中对此进行编码很容易。因此,如果您不想抑制该规则,另一种选择是将您的方法分成两部分:
In reality effectively calling dispose twice on the memory stream won't cause any problems, it would be easy to code against this inside the MemoryStream class and on testing Microsoft appear to have. Therefore, if you didn't want to supress the rule another alternative is to split your method in two:
这有时是运行代码分析的问题,有时你根本无法获胜,你必须选择两害相权取其轻。
在这种情况下,我相信正确的实施是第二个例子。为什么?根据.NET Reflector,
GZipStream.Dispose()
的实现将处理GZipStream 拥有 MemoryStream 为您提供MemoryStream
。下面是
GZipStream
类的相关部分:由于您不想完全禁用该规则,因此您可以仅使用
CodeAnalysis.SupressMessage
属性来抑制此方法。注意:您需要填写完整的规则名称(即
CA2000:?
),因为我从您发布的错误消息中不知道它是什么。HTH,
编辑:
@CodeInChaos:
更深入地了解实现
DeflateStream.Dispose
我相信它仍然会为您处理 MemoryStream,而不管 leftOpen 选项如何,因为它调用base。 Dispose()
。编辑 忽略上面关于
DeflateStream.Dispose
的内容。我正在查看 Reflector 中的错误实现。详情请参阅评论。That is sometimes the problem with running CodeAnalysis, you sometimes you simply cannot win and you have to choose the lesser evil™.
In this situation, I believe the correct implementation is the second example. Why? According to .NET Reflector, the implementation of
GZipStream.Dispose()
will dispose of the theMemoryStream
for you as GZipStream owns the MemoryStream.Relevant parts of
GZipStream
class below:As you wouldn't want to disable the rule entirely, you can suppress for this method only using using the
CodeAnalysis.SupressMessage
attribute.Note: You will have fill in the full rule name (i.e.
CA2000:?
) as I did not know what it was from the error message you posted.HTH,
EDIT:
@CodeInChaos:
Looking deeper at the implementation
DeflateStream.Dispose
I believe it still will dispose of the MemoryStream for you regardless of the leaveOpen option as it calls thebase.Dispose()
.EDIT Ignore the above about
DeflateStream.Dispose
. I was looking at the wrong implementation in Reflector. See comments for details.你必须走老路:
我知道这看起来很糟糕,但这是平息警告的唯一方法。
就我个人而言,我不喜欢编写这样的代码,因此只需在 Dispose 调用应该是幂等的基础上抑制多个 dispose 警告(在适用的情况下)(在本例中就是如此)。
You have to go old school:
Looks horrible I know, but is the only way to appease the warning.
Personally, I dislike writing code like this, so just suppress the multiple dispose warning (where applicable) on the basis that Dispose calls should be idempotent (and is in this case).