CA2202,如何解决这种情况

时间:2010-09-30 14:41:12

标签: c# .net code-analysis fxcop

有人可以告诉我如何从以下代码中删除所有CA2202警告吗?

public static byte[] Encrypt(string data, byte[] key, byte[] iv)
{
    using(MemoryStream memoryStream = new MemoryStream())
    {
        using (DESCryptoServiceProvider cryptograph = new DESCryptoServiceProvider())
        {
            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write))
            {
                using(StreamWriter streamWriter = new StreamWriter(cryptoStream))
                {
                    streamWriter.Write(data);
                }
            }
        }
        return memoryStream.ToArray();
    }
}
  

警告7 CA2202:Microsoft.Usage:对象'cryptoStream'可以在方法'CryptoServices.Encrypt(string,byte [],byte [])'中多次处理。为避免生成System.ObjectDisposedException,不应在对象上多次调用Dispose:Lines:34

     

警告8 CA2202:Microsoft.Usage:对象'memoryStream'可以在方法'CryptoServices.Encrypt(string,byte [],byte [])'中多次处理。为避免生成System.ObjectDisposedException,您不应在对象上多次调用Dispose:Lines:34,37

您需要Visual Studio代码分析才能看到这些警告(这些不是c#编译器警告)。

12 个答案:

答案 0 :(得分:134)

在这种情况下,您应该取消警告。处理一次性用品的代码应该是一致的,您不必关心其他类别对您创建的一次性用品拥有所有权,并且还要对其进行调用{.1}}。

Dispose

更新:在IDisposable.Dispose文档中,您可以阅读以下内容:

  

如果多次调用对象的Dispose方法,则该对象必须忽略第一个之后的所有调用。如果多次调用Dispose方法,则该对象不得抛出异常。

可以说这个规则的存在是为了让开发人员可以在一系列一次性用品中明智地使用[SuppressMessage("Microsoft.Usage", "CA2202:Do not dispose objects multiple times")] public static byte[] Encrypt(string data, byte[] key, byte[] iv) { using (var memoryStream = new MemoryStream()) { using (var cryptograph = new DESCryptoServiceProvider()) using (var cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write)) using (var streamWriter = new StreamWriter(cryptoStream)) { streamWriter.Write(data); } return memoryStream.ToArray(); } } 语句,就像我上面所示(或者这只是一个很好的副作用)。出于同样的原因,CA2202没有任何用处,应该在项目方面进行抑制。真正的罪魁祸首是using的错误实施,而CA1065应该照顾(如果它由你负责)。

答案 1 :(得分:41)

嗯,这是准确的,这些流上的Dispose()方法将被多次调用。 StreamReader类将获取cryptoStream的“所有权”,因此处理streamWriter也将处理cryptoStream。类似地,CryptoStream类接管memoryStream的责任。

这些并不是真正的错误,这些.NET类可以适应多个Dispose()调用。但是如果你想摆脱警告,那么你应该删除这些对象的using语句。如果代码抛出异常,在推理会发生什么时会让自己痛苦一点。或者使用属性关闭警告。或者只是忽略警告,因为它很愚蠢。

答案 2 :(得分:9)

当处置StreamWriter时,它会自动处理已包裹的Stream(此处为CryptoStream)。 CryptoStream还会自动处理已包裹的Stream(此处为MemoryStream)。

因此MemoryStream使用语句都会处理您的CryptoStream。您的CryptoStreamStreamWriter和外使用语句处理。


经过一些实验,似乎不可能完全摆脱警告。理论上,MemoryStream需要处理,但理论上你不能再访问它的ToArray方法了。实际上,不需要处理MemoryStream,因此我将使用此解决方案并抑制CA2000警告。

var memoryStream = new MemoryStream();

using (var cryptograph = new DESCryptoServiceProvider())
using (var writer = new StreamWriter(new CryptoStream(memoryStream, ...)))
{
    writer.Write(data);
}

return memoryStream.ToArray();

答案 3 :(得分:8)

我会使用#pragma warning disable执行此操作。

.NET Framework指南建议以可以多次调用的方式实现IDisposable.Dispose。来自the MSDN description of IDisposable.Dispose

  

如果多次调用Dispose方法

,则该对象不得抛出异常

因此警告似乎几乎毫无意义:

  

为避免生成System.ObjectDisposedException,不应在对象上多次调用Dispose

我想可以认为,如果您使用的是严格执行的IDisposable对象,并且不符合标准实施指南,则警告可能会有所帮助。但是当你使用.NET Framework中的类时,我会说使用#pragma来抑制警告是安全的。恕我直言,这比suggested in the MSDN documentation for this warning更好。

答案 4 :(得分:2)

我的代码中遇到了类似的问题。

看起来整个CA2202事件都被触发了,因为如果构造函数(CA2000)中发生异常,MemoryStream可以处理。

这可以这样解决:

 1 public static byte[] Encrypt(string data, byte[] key, byte[] iv)
 2 {
 3    MemoryStream memoryStream = GetMemoryStream();
 4    using (DESCryptoServiceProvider cryptograph = new DESCryptoServiceProvider())
 5    {
 6        CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write);
 7        using (StreamWriter streamWriter = new StreamWriter(cryptoStream))
 8        {
 9            streamWriter.Write(data);
10            return memoryStream.ToArray();
11        }
12    }
13 }
14
15 /// <summary>
16 /// Gets the memory stream.
17 /// </summary>
18 /// <returns>A new memory stream</returns>
19 private static MemoryStream GetMemoryStream()
20 {
21     MemoryStream stream;
22     MemoryStream tempStream = null;
23     try
24     {
25         tempStream = new MemoryStream();
26
27         stream = tempStream;
28         tempStream = null;
29     }
30     finally
31     {
32         if (tempStream != null)
33             tempStream.Dispose();
34     }
35     return stream;
36 }

请注意,我们必须在最后memoryStream语句(第10行)中返回using,因为cryptoStream会在第11行处置(因为它在streamWriter中使用{ {1}}声明),导致using也被置于第11行(因为memoryStream用于创建memoryStream)。

至少这段代码对我有用。

修改

听起来很有趣,我发现如果用以下代码替换cryptoStream方法,

GetMemoryStream

你得到的结果相同。

答案 5 :(得分:1)

cryptuffream基于内存流。

似乎正在发生的事情是当处置冷凝流时(在使用结束时)也会释放存储器流,然后再次处理存储器流。

答案 6 :(得分:1)

我想以正确的方式解决这个问题 - 即不要压制警告并正确处理所有一次性物品。

我将3个流中的2个作为字段取出并将它们放在我班级的Dispose()方法中。是的,实现IDisposable界面可能不一定是您正在寻找的,但与代码中所有随机位置的dispose()调用相比,该解决方案看起来非常干净。

public class SomeEncryption : IDisposable
    {
        private MemoryStream memoryStream;

        private CryptoStream cryptoStream;

        public static byte[] Encrypt(string data, byte[] key, byte[] iv)
        {
             // Do something
             this.memoryStream = new MemoryStream();
             this.cryptoStream = new CryptoStream(this.memoryStream, encryptor, CryptoStreamMode.Write);
             using (var streamWriter = new StreamWriter(this.cryptoStream))
             {
                 streamWriter.Write(plaintext);
             }
            return memoryStream.ToArray();
        }

       public void Dispose()
        { 
             this.Dispose(true);
             GC.SuppressFinalize(this);
        }

       protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (this.memoryStream != null)
                {
                    this.memoryStream.Dispose();
                }

                if (this.cryptoStream != null)
                {
                    this.cryptoStream.Dispose();
                }
            }
        }
   }

答案 7 :(得分:0)

偏离主题,但我建议您使用不同的格式化技术对using进行分组:

using (var memoryStream = new MemoryStream())
{
    using (var cryptograph = new DESCryptoServiceProvider())
    using (var encryptor = cryptograph.CreateEncryptor(key, iv))
    using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
    using (var streamWriter = new StreamWriter(cryptoStream))
    {
        streamWriter.Write(data);
    }

    return memoryStream.ToArray();
}

我还提倡在这里使用var来避免重复真正的长名称。

P.S。感谢@ShellShock指出我不能省略第一个using的大括号,因为它会使memoryStream语句中的return超出范围。

答案 8 :(得分:0)

避免所有使用并使用嵌套的Dispose-Calls!

    public static byte[] Encrypt(string data, byte[] key, byte[] iv)
    {
        MemoryStream memoryStream = null;
        DESCryptoServiceProvider cryptograph = null;
        CryptoStream cryptoStream = null;
        StreamWriter streamWriter = null;

        try
        {
            memoryStream = new MemoryStream();
            cryptograph = new DESCryptoServiceProvider();
            cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write);
            streamWriter = new StreamWriter(cryptoStream);

            streamWriter.Write(data);
            return memoryStream.ToArray();
        }
        finally 
        {
            if(streamWriter != null)
                streamWriter.Dispose();
            else if(cryptoStream != null)
                cryptoStream.Dispose();
            else if(memoryStream != null)
                memoryStream.Dispose();

            if (cryptograph != null)
                cryptograph.Dispose();
        }
    }

答案 9 :(得分:0)

我只是想解开代码,以便我们可以看到对Dispose的多次调用:

memoryStream = new MemoryStream()
cryptograph = new DESCryptoServiceProvider()
cryptoStream = new CryptoStream()
streamWriter = new StreamWriter()

memoryStream.Dispose(); //implicitly owned by cryptoStream
cryptoStream.Dispose(); //implicitly owned by streamWriter
streamWriter.Dispose(); //through a using

cryptoStream.Dispose(); //INVALID: second dispose through using
cryptograph.Dispose(); //through a using
memorySTream.Dipose(); //INVALID: second dispose through a using

return memoryStream.ToArray(); //INVALID: accessing disposed memoryStream

虽然大多数.NET类(希望)能够抵御多次调用.Dispose的错误,但不是所有类都可以防止程序员滥用。

FX Cop知道这一点,并警告你。

你有几个选择;

  • 只能在任何对象上调用Dispose一次;不要使用using
  • 继续调用dispose两次,并希望代码不会崩溃
  • 禁止警告

答案 10 :(得分:-1)

我使用了这种代码,它接受byte []并返回byte []而不使用流

public static byte[] Encrypt(byte[] data, byte[] key, byte[] iv)
{
  DES des = new DES();
  des.BlockSize = 128;
  des.Mode = CipherMode.CBC;
  des.Padding = PaddingMode.Zeros;
  des.IV = IV
  des.Key = key
  ICryptoTransform encryptor = des.CreateEncryptor();

  //and finaly operations on bytes[] insted of streams
  return encryptor.TransformFinalBlock(plaintextarray,0,plaintextarray.Length);
}

这样你所要做的就是使用编码从字符串转换为byte []。

答案 11 :(得分:-2)

此编译无需警告:

    public static byte[] Encrypt(string data, byte[] key, byte[] iv)
    {
        MemoryStream memoryStream = null;
        DESCryptoServiceProvider cryptograph = null;
        CryptoStream cryptoStream = null;
        StreamWriter streamWriter = null;
        try
        {
            memoryStream = new MemoryStream();
            cryptograph = new DESCryptoServiceProvider();
            cryptoStream = new CryptoStream(memoryStream, cryptograph.CreateEncryptor(key, iv), CryptoStreamMode.Write);
            var result = memoryStream;              
            memoryStream = null;
            streamWriter = new StreamWriter(cryptoStream);
            cryptoStream = null;
            streamWriter.Write(data);
            return result.ToArray();
        }
        finally
        {
            if (memoryStream != null)
                memoryStream.Dispose();
            if (cryptograph != null)
                cryptograph.Dispose();
            if (cryptoStream != null)
                cryptoStream.Dispose();
            if (streamWriter != null)
                streamWriter.Dispose();
        }
    }

编辑以回应评论: 我刚刚再次验证了此代码不会生成警告,而原始代码则会生成警告。 在原始代码中,CryptoStream.Dispose()MemoryStream().Dispose()实际上被调用了两次(这可能是也可能不是问题)。

修改后的代码的工作方式如下:只要将处置的责任转移到另一个对象,引用就会设置为null。例如。在调用memoryStream构造函数成功后,null设置为CryptoStream。在调用cryptoStream构造函数成功后,null设置为StreamWriter。如果没有发生异常,streamWriter块中会放置finally,并依次处理CryptoStreamMemoryStream