更正此代码的IDisposable实现

Ric*_*h S 11 .net c# 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这工作正常,但是当我对它运行代码分析时,它会出现以下消息

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.
Run Code Online (Sandbox Code Playgroud)

就我而言,当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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这就是......

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.
Run Code Online (Sandbox Code Playgroud)

我不能赢...(除非我遗漏了一些明显的东西)我已经尝试了各种各样的事情,比如试试/最后围绕块,并在那里处理MemoryStream,但它要么说我正在处理两次,或根本没有!

Cod*_*aos 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();
}
Run Code Online (Sandbox Code Playgroud)

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


Den*_*nis 1

这有时是运行CodeAnalysis的问题,有时您根本无法获胜,您必须选择较小的邪恶\xe2\x84\xa2。

\n\n

在这种情况下,我相信正确的实施是第二个例子。为什么?根据.NET Reflector ,实现将为您GZipStream.Dispose()处理GZipStream拥有MemoryStream 。MemoryStream

\n\n

GZipStream以下课程的相关部分:

\n\n
public GZipStream(Stream stream, CompressionMode mode, bool leaveOpen)\n{\n    this.deflateStream = new DeflateStream(stream, mode, leaveOpen, true);\n}\n\nprotected override void Dispose(bool disposing)\n{\n    try\n    {\n        if (disposing && (this.deflateStream != null))\n        {\n            this.deflateStream.Close();\n        }\n        this.deflateStream = null;\n    }\n    finally\n    {\n        base.Dispose(disposing);\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

由于您不想完全禁用该规则,因此您可以仅使用该CodeAnalysis.SupressMessage属性来抑制此方法。

\n\n
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability ", "CA2000:?", Justification = "MemoryStream will be disposed by the GZipStream.")]\n
Run Code Online (Sandbox Code Playgroud)\n\n

注意:您需要填写完整的规则名称(即CA2000:?),因为我不知道您发布的错误消息中的名称是什么。

\n\n

哈特哈,

\n\n

编辑:

\n\n

@CodeInChaos:

\n\n

更深入地研究实现,DeflateStream.Dispose 我相信它仍然会为您处理 MemoryStream,而不管 leftOpen 选项如何,因为它调用base.Dispose().

\n\n

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

\n