更正此代码的IDisposable实现

时间:2011-11-18 12:43:15

标签: c# .net code-analysis 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为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,但它要么说我处理它两次,或根本不处理!!

5 个答案:

答案 0 :(得分:6)

除了处理问题外,您的代码也被破坏了。您应该在回读数据之前关闭zip流。

ToArray()上还有一个MemoryStream方法,无需亲自实现。

using (MemoryStream ms = new MemoryStream())
{
    using (GZipStream gz = new GZipStream(ms, CompressionMode.Compress,true))
    {
        gz.Write(CompressMe, 0, CompressMe.Length);
    }
    return ms.ToArray();
}

我只是压制警告,因为这是误报。代码分析可以为您服务,而不是相反。

答案 1 :(得分: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)

无法处理流。

答案 2 :(得分:2)

实际上,有效地在内存流上调用两次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;
        }
    }

答案 3 :(得分:1)

这有时是运行 CodeAnalysis 的问题,有时你根本无法获胜,你必须选择较小的邪恶™。

在这种情况下,我认为正确的实现是第二个例子。为什么?根据{{​​3}},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选项如何,它都会为你处理MemoryStream,因为它调用了base.Dispose()

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

答案 4 :(得分: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();
        }
    }
}

我知道这看起来很可怕,但却是安抚警告的唯一方法。

就个人而言,我不喜欢编写这样的代码,所以只需要根据Dispose调用应该是幂等的(在这种情况下)来抑制多次处置警告(如果适用)。