CA2202, how to solve this case

后端 未结 12 958
闹比i
闹比i 2020-11-22 08:18

Can anybody tell me how to remove all CA2202 warnings from the following code?

public static byte[] Encrypt(string data, byte[] key, byte[] iv)
{
    us         


        
相关标签:
12条回答
  • 2020-11-22 08:33

    I just wanted to unwrap the code so we can see multiple calls to Dispose on the objects. The reality is that you are calling Dispose on objects twice:

    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
    

    While most .NET class are (hopefully) resilient against the mistake of multiple calls to .Dispose, not all classes are as defensive against programmer misuse.

    Yes, the canonical documentation says that all class must be immune to programmer misuse from multiple calls to .Dispose:

    The object must not throw an exception if its Dispose method is called multiple times

    But this is the real world - where we're trying to eliminate bugs; not cause them. FX Cop knows this, and warns you.

    You have a few choices;

    • only call Dispose once on any object; don't use using
    • keep calling dispose twice, and hope the code doesn't crash
    • suppress the warning
    0 讨论(0)
  • 2020-11-22 08:34

    I used this kind of code that takes byte[] and return byte[] without using streams

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

    This way all you have to do is conversion from string to byte[] using encodings.

    0 讨论(0)
  • 2020-11-22 08:37

    I would do this using #pragma warning disable.

    The .NET Framework Guidelines recommend to implement IDisposable.Dispose in such a way that it can be called multiple times. From the MSDN description of IDisposable.Dispose:

    The object must not throw an exception if its Dispose method is called multiple times

    Therefore the warning seems to be almost meaningless:

    To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object

    I guess it could be argued that the warning may be helpful if you're using a badly-implemented IDisposable object that does not follow the standard implementation guidelines. But when using classes from the .NET Framework like you are doing, I'd say it's safe to suppress the warning using a #pragma. And IMHO this is preferable to going through hoops as suggested in the MSDN documentation for this warning.

    0 讨论(0)
  • 2020-11-22 08:40

    The cryptostream is based on the memorystream.

    What appears to be happening is that when the crypostream is disposed (at end of using) the memorystream is also disposed, then the memorystream is disposed again.

    0 讨论(0)
  • 2020-11-22 08:40

    I wanted to solve this the right way - that is without suppressing the warnings and rightly disposing all disposable objects.

    I pulled out 2 of the 3 streams as fields and disposed them in the Dispose() method of my class. Yes, implementing the IDisposable interface might not necessarily be what you are looking for but the solution looks pretty clean as compared to dispose() calls from all random places in the code.

    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();
                    }
                }
            }
       }
    
    0 讨论(0)
  • 2020-11-22 08:41

    You should suppress the warnings in this case. Code that deals with disposables should be consistent, and you shouldn't have to care that other classes take ownership of the disposables you created and also call Dispose on them.

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

    UPDATE: In the IDisposable.Dispose documentation you can read this:

    If an object's Dispose method is called more than once, the object must ignore all calls after the first one. The object must not throw an exception if its Dispose method is called multiple times.

    It can be argued that this rule exists so that developers can employ the using statement sanely in a cascade of disposables, like I've shown above (or maybe this is just a nice side-effect). By the same token, then, CA2202 serves no useful purpose, and it should be suppressed project-wise. The real culprit would be a faulty implementation of Dispose, and CA1065 should take care of that (if it's under your responsibility).

    0 讨论(0)
提交回复
热议问题