此代码的正确 IDisposable 实现

发布于 2024-12-16 11:33:12 字数 1687 浏览 0 评论 0原文

我有以下代码

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 被处置时,它会使底层流(毫秒)保持打开状态,因为到构造函数的最后一个参数(leaveOpen=true)。

如果我稍微改变我的代码..删除 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.

我无法获胜..(除非我错过了一些明显的东西)我尝试过各种事情,比如在块周围放置一个 try/finally ,以及在那里处置 MemoryStream,但它要么说我要处置它两次,要么根本不处置!

I have the following 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;
        }
    }
}

This works fine, but when I run code analysis on it, it comes up with the following message

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.

As far as I'm concerned, when the GZipStream is Disposed, it leaves the underlying Stream (ms) open, due to the last parameter of the constructor (leaveOpen=true).

If I change my code slightly.. remove the 'using' block around the MemoryStream and change the 'leaveOpen' parameter to 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;
    }
}

This then comes up with..

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.

I can't win.. (unless I'm missing something obvious) I've tried various things, like putting a try/finally around the block, and Disposing of the MemoryStream in there, but it either says that I'm disposing of it twice, or not at all !!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

饭团 2024-12-23 11:33:12

除了处理问题之外,您的代码也被破坏了。您应该在读回数据之前关闭 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();
}

我只是抑制警告,因为它是误报。代码分析是为您服务的,而不是相反。

Apart from the disposing issue, your code is also broken. You should close the zip stream before reading back the data.

Also there is already a ToArray() method on MemoryStream, no need to implement that yourself.

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

I'd simply suppress the warning, since it's a false positive. Code analysis is there to serve you, not the other way round.

亽野灬性zι浪 2024-12-23 11:33:12

来自 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)

失败,则不会处理该流。

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

It is the try...finally that is missing from your solution that causes the second message.

If this:

GZipStream gz = new GZipStream(ms, CompressionMode.Compress, false)

fails the stream will not be disposed.

烟沫凡尘 2024-12-23 11:33:12

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

In reality effectively calling dispose twice on the memory stream won't cause any problems, it would be easy to code against this inside the MemoryStream class and on testing Microsoft appear to have. Therefore, if you didn't want to supress the rule another alternative is to split your method in two:

    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;
        }
    }
云醉月微眠 2024-12-23 11:33:12

这有时是运行代码分析的问题,有时你根本无法获胜,你必须选择两害相权取其轻。

在这种情况下,我相信正确的实施是第二个例子。为什么?根据.NET ReflectorGZipStream.Dispose()的实现将处理GZipStream 拥有 MemoryStream 为您提供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 我相信它仍然会为您处理 MemoryStream,而不管 leftOpen 选项如何,因为它调用 base。 Dispose()

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

That is sometimes the problem with running CodeAnalysis, you sometimes you simply cannot win and you have to choose the lesser evil™.

In this situation, I believe the correct implementation is the second example. Why? According to .NET Reflector, the implementation of GZipStream.Dispose() will dispose of the the MemoryStream for you as GZipStream owns the MemoryStream.

Relevant parts of GZipStream class below:

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

As you wouldn't want to disable the rule entirely, you can suppress for this method only using using the CodeAnalysis.SupressMessage attribute.

[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability ", "CA2000:?", Justification = "MemoryStream will be disposed by the GZipStream.")]

Note: You will have fill in the full rule name (i.e. CA2000:?) as I did not know what it was from the error message you posted.

HTH,

EDIT:

@CodeInChaos:

Looking deeper at the implementation DeflateStream.Dispose I believe it still will dispose of the MemoryStream for you regardless of the leaveOpen option as it calls the base.Dispose().

EDIT Ignore the above about DeflateStream.Dispose. I was looking at the wrong implementation in Reflector. See comments for details.

挽梦忆笙歌 2024-12-23 11:33:12

你必须走老路:

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 警告(在适用的情况下)(在本例中就是如此)。

You have to go old school:

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

Looks horrible I know, but is the only way to appease the warning.

Personally, I dislike writing code like this, so just suppress the multiple dispose warning (where applicable) on the basis that Dispose calls should be idempotent (and is in this case).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文