Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
356 views
in Technique[技术] by (71.8m points)

c# - Code Anlysis Rule CA2000 / CA2202

I am trying to ensure my coding follows correct disposal of objects so I am enforcing these rules as errors. But I am having trouble with this section of code

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;

class MyClass
{  
    public String ToXml()
    {
        var objSerializer = 
            new DataContractSerializer(GetType());
        var objStream = new MemoryStream();
        StreamReader objReader;

        String strResult;
        try
        {
            // Serialize the object
            objSerializer.WriteObject(objStream, this);

            // Move to start of stream to read out contents
            objStream.Seek(0, SeekOrigin.Begin);

            objReader = new StreamReader(objStream);

            try
            {
                // Read Contents into a string
                strResult = objReader.ReadToEnd();
            }
            finally
            {
                objReader.Dispose();
            }
        }
        finally
        {
            if (objStream != null)
            {
                // objStream.Dispose();
            }
        }

        return strResult;
    }
}

If I comment out objStream.Dispose() I get CA2000 as I am not disposing the object but if I remove the comment it then says I am disposing more than once.

What else is disposing the object? or am I just doing this wrong when dealing with multiple streams?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This scenario has been annoying me as well now. Every couple of years I decide to refresh myself of the code analysis "rules" by running fxcop or the now built-in code analysis in Visual Studio.

I originally wrote this code, thinking I was being a good citizen for properly using usings to dispose:

using (MemoryStream msDecrypt = new MemoryStream())
{
    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
    {
        csDecrypt.Write(eop.m_Ciphertext, 0, eop.m_Ciphertext.Length);
    }

    decrypted = msDecrypt.ToArray();
}

This block of code results in a CA2202 "Do not dispose objects multiple times" The great irony about this rule, is that it's not really about a problem with your code, as much as it is protecting you about a problem in others code. Microsoft has always had a decent amount of documentation about how the Dispose pattern (http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx) should be implmented. However, by looking at the details of this code analysis rule (http://msdn.microsoft.com/en-us/library/ms182334.aspx) it reveals the purpose of this rule

"A correctly implemented Dispose method can be called multiple times without throwing an exception. However, this is not guaranteed and to avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object."

In short, this rule is all about protecting yourself from people who don't follow the rules.

Naturally I modified the code to look like this:

MemoryStream msDecrypt = new MemoryStream()    
//using (MemoryStream msDecrypt = new MemoryStream())
//{
    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write))
    {
        csDecrypt.Write(eop.m_Ciphertext, 0, eop.m_Ciphertext.Length);
    }

    decrypted = msDecrypt.ToArray();
//}

Now everybody on this stack overflow post is painfully aware of the new problem, our friend CA2000 "Dispose objects before losing scope" ... so at this point I just face palmed for a minute. Did a few Google searches, and found this post. That's when it dawned on me, to pass for both CA rules, you need to ensure everything is disposed once and only once for all code branches. So I set out to do this, which isn't a hard problem once you realize this is what you need to do.

Naturally, the code evolved to this:

MemoryStream msDecrypt = null;
CryptoStream csDecrypt = null;

try
{    
    msDecrypt = new MemoryStream();
    csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Write);

    csDecrypt.Write(eop.m_Ciphertext, 0, eop.m_Ciphertext.Length);
    csDecrypt.FlushFinalBlock();
    decrypted = msDecrypt.ToArray();
}
finally
{
    if (csDecrypt != null)
    {
        csDecrypt.Dispose();
    }
    else if (msDecrypt != null)
    {
        msDecrypt.Dispose();
    }

}

Finally, I had code that didn't result in a CA2000 or CA2202. The moral of the story is that the USING statement, is a lot less valuable than it was in the past now that code analysis rules have evolved in this way.

There are a few different ways you can write the code to make this work, I just chose a way that doesn't mix explicit calls to dispose with using statements, because I believe this to be simpler to read as well as structured in a way that would prevent someone from just going and wrapping another using around it, resulting in the originally issue unknowingly.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...