CA2202, how to solve this case

后端 未结 12 968
闹比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:43

    I was faced with similar issues in my code.

    Looks like the whole CA2202 thing is triggered because MemoryStream can be disposed if exception occurs in constructor (CA2000).

    This could be resolved like this:

     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 }
    

    Notice that we have to return the memoryStream inside the last using statement (line 10) because cryptoStream gets disposed at line 11 (because it's used in streamWriter using statement), which leads memoryStream to get also disposed at line 11 (because memoryStream is used to create the cryptoStream).

    At least this code worked for me.

    EDIT:

    Funny as it may sound, I discovered that if you replace the GetMemoryStream method with the following code,

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

    you get the same result.

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

    Avoid all usings and use nested 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();
            }
        }
    
    0 讨论(0)
  • 2020-11-22 08:52

    Well, it is accurate, the Dispose() method on these streams will be called more than once. The StreamReader class will take 'ownership' of the cryptoStream so disposing streamWriter will also dispose cryptoStream. Similarly, the CryptoStream class takes over responsibility for the memoryStream.

    These are not exactly real bugs, these .NET classes are resilient to multiple Dispose() calls. But if you want to get rid of the warning then you should drop the using statement for these objects. And pain yourself a bit when reasoning what will happen if the code throws an exception. Or shut-up the warning with an attribute. Or just ignore the warning since it is silly.

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

    When a StreamWriter is disposed, it will automatically dispose the wrapped Stream (here: the CryptoStream). CryptoStream also automatically disposes the wrapped Stream (here: the MemoryStream).

    So your MemoryStream is disposed both by the CryptoStream and the using statement. And your CryptoStream is disposed by the StreamWriter and the outer using statement.


    After some experimentation, it seems to be impossible to get rid of warnings completely. Theorectically, the MemoryStream needs to be disposed, but then you theoretically couldn't access its ToArray method anymore. Practically, a MemoryStream does not need to be disposed, so I'd go with this solution and suppress the CA2000 warning.

    var memoryStream = new MemoryStream();
    
    using (var cryptograph = new DESCryptoServiceProvider())
    using (var writer = new StreamWriter(new CryptoStream(memoryStream, ...)))
    {
        writer.Write(data);
    }
    
    return memoryStream.ToArray();
    
    0 讨论(0)
  • 2020-11-22 08:56

    Off-topic but I would suggest you to use a different formatting technique for grouping usings:

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

    I also advocate using vars here to avoid repetitions of really long class names.

    P.S. Thanks to @ShellShock for pointing out I can't omit braces for first using as it would make memoryStream in return statement out of the scope.

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

    This compiles without warning:

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

    Edit in response to the comments: I just verified again that this code does not generate the warning, while the original one does. In the original code, CryptoStream.Dispose() and MemoryStream().Dispose() are actually called twice (which may or may not be a problem).

    The modified code works as follows: references are set to null, as soon as responsibilty for disposing is transferred to another object. E.g. memoryStream is set to null after the call to CryptoStream constructor succeeded. cryptoStream is set to null, after the call to StreamWriter constructor succeeded. If no exception occurs, streamWriter is disposed in the finally block and will in turn dispose CryptoStream and MemoryStream.

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