代码之家  ›  专栏  ›  技术社区  ›  testalino

CA2202,怎么解决这个案子

  •  98
  • testalino  · 技术社区  · 14 年前

    有人能告诉我如何从下面的代码中删除所有的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();
        }
    }
    

    警告8 CA2202:Microsoft.用法:对象“memoryStream”可以在方法中多次释放加密服务。加密(字符串,字节[],字节[])'。为了避免生成System.ObjectDisposedException对于一个对象,不应多次调用Dispose。语法:Lines:34,37

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

    12 回复  |  直到 10 年前
        1
  •  -3
  •   Henrik    12 年前

    编译时没有警告:

        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 设置为 无效的 打电话给 CryptoStream 构造函数成功。 cryptoStream 设置为 无效的 StreamWriter 构造函数成功。如果没有发生异常, streamWriter finally 标准接口 MemoryStream .

        2
  •  142
  •   IAmJersh    6 年前

    在这种情况下,您应该取消显示警告。处理可处置资产的代码应该是一致的,您不必关心其他类拥有您创建和调用的可处置资产的所有权 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();
      }
    }
    

    更新: IDisposable.Dispose 您可以阅读以下文档:

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

    可以说,这个规则的存在是为了让开发人员可以使用 using 处置 ,和 CA1065 应该处理好(如果这是你的责任)。

        3
  •  40
  •   Hans Passant    7 年前

        4
  •  9
  •   dtb    14 年前

    StreamWriter 被释放时,它将自动释放包装好的 Stream CryptoStream ). 标准接口 溪流 (此处: MemoryStream ).

    所以你的 记忆流 标准接口 使用 声明。你的呢 标准接口 流作者 使用


    经过一些试验,似乎不可能完全消除警告。理论上讲,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();
    
        5
  •  9
  •   to StackOverflow    14 年前

    #pragma warning disable .

    建议实施.NET框架指南我可以处理。处理以一种可以多次调用的方式。从 the MSDN description of IDisposable.Dispose :

    因此,这一警告似乎毫无意义:

    为了避免生成System.ObjectDisposedException对一个对象不应多次调用Dispose

    我想,如果您使用的IDisposable对象没有遵循标准实现指南,那么这个警告可能会有所帮助。但是,当像您现在这样使用.NET Framework中的类时,可以安全地使用#pragma来隐藏警告。而且我觉得这比像 suggested in the MSDN documentation for this warning .

        6
  •  2
  •   Jimi    13 年前

    看起来整个CA2202事件是因为 MemoryStream 如果构造函数(CA2000)中发生异常,则可以释放。

    可以这样解决:

     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 在最后一个 using cryptoStream 在第11行处理(因为它在 streamWriter 记忆流 也可以在第11行处理(因为 用于创建 密码流 ).

    至少这个代码对我有用。

    编辑:

    GetMemoryStream 方法使用以下代码,

    /// <summary>
    /// Gets a memory stream.
    /// </summary>
    /// <returns>A new memory stream</returns>
    private static MemoryStream GetMemoryStream()
    {
        return new MemoryStream();
    }
    

        7
  •  1
  •   Shiraz Bhaiji    14 年前

    密码流基于内存流。

    当crypostream被释放(在使用结束时),memorystream也被释放,然后memorystream再次被释放。

        8
  •  1
  •   divyanshm    10 年前

    我从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();
                    }
                }
            }
       }
    
        9
  •  0
  •   Dan Abramov    14 年前

    但我建议您使用不同的格式技术进行分组 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 这里是为了避免重复非常长的类名。

    使用 就像它会做的那样 memoryStream return 语句超出范围。

        10
  •  0
  •   Harry Saltzman    12 年前

    避免所有using并使用嵌套的Dispose调用!

        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();
            }
        }
    
        11
  •  0
  •   Ian Boyd    9 年前

    我只想打开代码,这样我们就可以看到对 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 ,不是 类是为了防止程序员的误用。

    你有一些选择;

    • 处置 在任何物体上一次;不要使用 using
        12
  •  -1
  •   Luka Rahne    14 年前

    我使用了这种不使用流的字节[]和返回字节[]的代码

    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[]。