2011-11-18 51 views
11

我有以下代码正确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布置,它留下底层的流(MS)开放,由于构造函数(leaveOpen = TRUE)的最后一个参数。

如果我稍微改变我的代码..去掉周围的MemoryStream的“使用”块,改变“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 /终于各地块,并在那里将MemoryStream的处置,但也不说,我处置它两次,或根本不!

+5

这是很奇怪的。从[msdn文档](http://msdn.microsoft.com/en-us/library/b1yfkh5e.aspx):[...] Dispose方法应该多次调用而不会抛出异常(ObjectDisposedException )。 – oleksii

+0

CA2000是一个巨大的皮塔饼。根据我的经验,它比genuiune警告产生更多的误报。所有[哭泣的狼](http://en.wikipedia.org/wiki/The_Boy_Who_Cried_Wolf)现在意味着我倾向于忽略/压制CA2000。 – LukeH

+1

你不能赢。在你的代码中修正bug,gz需要Flush()或者关闭以产生所有的字节。 –

回答

1

也就是说有时运行CodeAnalysis这个问题,你有时你根本无法赢得,你必须选择不幸中之大幸™。

在这种情况下,我认为正确的实施是第二个例子。为什么?据.NET ReflectorGZipStream.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选项的MemoryStream的,因为它会调用base.Dispose()

编辑忽略上述约DeflateStream.Dispose。我在Reflector中查看错误的实现。详情请参阅评论。

+0

这看起来像是转发处置作业内存流到'DeflateStream',这可能只会在'leaveOpen'参数为false时才会执行。 – CodesInChaos

+0

@CodeInChaos:更详细地更新了我的答案。我相信我已经正确解释了代码。 – Dennis

+0

它在调用'base.Dispose'之前将'_stream'字段设置为'null'。但在这种情况下,你发布的方法应该抛出... – CodesInChaos

3

this page in the 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) 

失败流将不被设置。

+0

我确实尝试了MSDN页面中的示例,但是出现了相同的代码分析错误。 –

+0

另外,如果GZip构造函数失败,它仍然应该正确地从“使用”块中退出。这就是想法吗? –

+0

这很奇怪......当我在回答中分析代码时,它不会产生任何错误/警告 –

0

你必须去老同学:

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(); 
     } 
    } 
} 

看起来可怕,我知道,不过是为了安抚警告的唯一途径。

就个人而言,我不喜欢写这样的代码,所以只是抑制多重处置是处置呼叫应幂等的(并且是在这种情况下)的基础上,警告(如适用)。

+0

是的,我倾向于同意,我确信我可以解决这个问题,使用{}命令应该照顾它似乎很疯狂像这样的东西,并简化代码。 –

+0

使用块是否必要,因为使用简单地是用于try/finally的syntally糖?难道所有的处置都不能在最后的块中发生吗? –

6
从处置问题

除此之外,你的代码也被打破。在读回数据之前,您应该关闭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(); 
} 

我只是简单地禁止警告,因为这是一个误报。代码分析服务于您,而不是相反。

+0

+1。我也相信这个错误是* false-positive *,并且在分析它之后选择最正确的实现并且抑制警告/规则。 – Dennis

+0

+1特别是对于这些应该为我们工作的工具,而不是我们为他们工作的观点。 –

+0

感谢您指出ToArray()方法。 –

2

实际上,在内存流中有效地调用两次处理不会导致任何问题,在MemoryStream类中进行编码并在Microsoft上进行测试似乎很容易。因此,如果你不想抑制这个规则,另一个选择是将你的方法分成两部分:

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; 
     } 
    } 
相关问题