这段代码正确的IDisposable实现
本文关键字:IDisposable 实现 段代码 代码 | 更新日期: 2023-09-27 18:15:00
我有以下代码
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被dispose时,由于构造函数的最后一个参数(leaveOpen=true),它使底层流(ms)打开。
如果我稍微改变我的代码…删除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,但它要么说我要处置它两次,要么根本不!!
除了处理问题之外,您的代码也被破坏了。您应该在读取数据之前关闭zip流。
在MemoryStream
上已经有一个ToArray()
方法,不需要自己实现。
using (MemoryStream ms = new MemoryStream())
{
using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress,true))
{
gz.Write(CompressMe, 0, CompressMe.Length);
}
return ms.ToArray();
}
我将简单地抑制警告,因为它是假阳性。代码分析是为你服务的,而不是为你服务的。
从本页的MSDN
Stream stream = null;
try
{
stream = new FileStream("file.txt", FileMode.OpenOrCreate);
using (StreamWriter writer = new StreamWriter(stream))
{
stream = null;
// Use the writer object...
}
}
finally
{
if(stream != null)
stream.Dispose();
}
这是尝试…最后,您的解决方案中缺少了导致第二条消息的内容。
如果:
GZipStream gz = new GZipStream(ms, CompressionMode.Compress, false)
失败,流将不会被处理。
实际上,在内存流上有效地调用dispose两次不会引起任何问题,在MemoryStream类中针对这一点编写代码很容易,并且在测试中微软似乎有。因此,如果您不想抑制规则,另一种选择是将您的方法分成两部分:
public static byte[] Compress(byte[] CompressMe)
{
using (MemoryStream ms = new MemoryStream())
{
return Compress(CompressMe, ms);
}
}
public static byte[] Compress(byte[] CompressMe, MemoryStream ms)
{
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;
}
}
这就是有时运行CodeAnalysis的问题,有时你根本无法获胜,你必须选择较轻的邪恶™。
在这种情况下,我认为正确的实现是第二个例子。为什么?根据。net Reflector, GZipStream.Dispose()
的实现将为您处置MemoryStream
作为GZipStream拥有MemoryStream。
GZipStream
类相关部分如下:
public GZipStream(Stream stream, CompressionMode mode, bool leaveOpen)
{
this.deflateStream = new DeflateStream(stream, mode, leaveOpen, true);
}
protected override void Dispose(bool disposing)
{
try
{
if (disposing && (this.deflateStream != null))
{
this.deflateStream.Close();
}
this.deflateStream = null;
}
finally
{
base.Dispose(disposing);
}
}
由于您不想完全禁用该规则,因此您可以仅使用CodeAnalysis.SupressMessage
属性来禁用此方法。
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability ", "CA2000:?", Justification = "MemoryStream will be disposed by the GZipStream.")]
注意:您将填写完整的规则名称(即CA2000:?
),因为我不知道您发布的错误消息是什么。
HTH,
编辑:@CodeInChaos:
深入了解DeflateStream.Dispose
的实现,我相信它仍然会为您处置内存流,而不管leaveOpen选项,因为它调用base.Dispose()
EDIT忽略以上关于DeflateStream.Dispose
的内容。我在看错误的实现在反射器。有关详细信息,请参阅注释。
你得走老路:
public static byte[] Compress(byte[] CompressMe)
{
MemoryStream ms = null;
GZipStream gz = null;
try
{
ms = new MemoryStream();
gz = new GZipStream(ms, CompressionMode.Compress, true);
gz.Write(CompressMe, 0, CompressMe.Length);
gz.Flush();
return ms.ToArray();
}
finally
{
if (gz != null)
{
gz.Dispose();
}
else if (ms != null)
{
ms.Dispose();
}
}
}
看起来很可怕,我知道,但这是唯一的方法来平息警告。
就我个人而言,我不喜欢这样写代码,所以在dispose调用应该是幂等的(在这种情况下)的基础上,只抑制多次dispose警告(在适用的情况下)。