2011-11-18 83 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; 
     } 
    }