簡體   English   中英

更正此代碼的IDisposable實現

[英]Correct IDisposable implementation for this 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;
        }
    }
}

這工作正常,但是當我對它運行代碼分析時,它會出現以下消息

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為Disposed時,由於構造函數的最后一個參數(leaveOpen = true),它會使底層Stream(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.

我不能贏...(除非我遺漏了一些明顯的東西)我已經嘗試了各種各樣的事情,比如試試/最終圍繞塊,並在那里處理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類中進行編碼並且在測試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;
        }
    }

這有時是運行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中查看錯誤的實現。 請參閱評論了解詳情

你必須去上學:

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警告(如果適用)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM