代码分析规则CA2000 / CA2202

时间:2012-04-13 11:09:59

标签: c# code-analysis stylecop

我正在努力确保我的编码遵循正确的对象处理方式,因此我将这些规则强制执行为错误。但我在使用这段代码时遇到了麻烦

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

如果我评论objStream.Dispose()我得到了CA2000,因为我没有处理该对象,但是如果我删除了评论,那么我说我处理的次数不止一次。

还有什么处理对象?或者我在处理多个流时只是做错了吗?

3 个答案:

答案 0 :(得分:8)

这个场景现在也让我烦恼。每隔几年,我决定通过在Visual Studio中运行fxcop或现在的内置代码分析来刷新自己的代码分析“规则”。

我最初编写这段代码,认为我是一个善于使用uses处理的好公民:

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

这段代码导致 CA2202“不要多次丢弃对象”关于这条规则的讽刺之处在于它并不是真正与你的代码有关的问题,因为它是保护您的其他代码中的问题。 Microsoft一直有大量关于如何实现Dispose模式(http://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx)的文档。但是,通过查看此代码分析规则(http://msdn.microsoft.com/en-us/library/ms182334.aspx)的详细信息,它揭示了此规则的目的

  

“可以多次调用正确实现的Dispose方法   没有抛出异常。但是,这并不能保证   避免生成不应调用的System.ObjectDisposedException   在一个物体上多次处理。“

简而言之,这条规则就是保护自己免受那些不遵守规则的人的伤害。

当然,我将代码修改为如下所示:

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

现在每个人都在这个堆栈溢出帖子上痛苦地意识到新问题,我们的朋友 CA2000“在丢失范围之前处理对象” ...所以在这一点上我只是面对了一分钟。做过几次谷歌搜索,发现这篇文章。就在我突然想到要传递两个CA规则的时候,你需要确保所有代码分支只处理一次并且只处理一次。所以我开始这样做,一旦你意识到这是你需要做的事情,这不是一个难题。

当然,代码演变为:

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

}

最后,我的代码没有导致CA2000或CA2202。这个故事的寓意在于,使用USING语句比现在的代码分析规则以这种方式发展的价值要低得多。

有几种不同的方法可以编写代码来实现这个功能,我只是选择了一种不会将显式调用与dispose混合使用的方法,因为我认为这样做更简单易读和结构化以某种方式阻止某人在其周围使用并包裹另一个人,从而在不知情的情况下导致最初的问题。

答案 1 :(得分:1)

如果您丢弃StreamReader,则还要处理基础流。

如果你注释掉objStream.Dispose(),那么你甚至在进入嵌套的try块之前就会遇到抛出异常的可能性 - 这将导致你的流不被处理掉。

这里有一个很好的解释: Does disposing streamreader close the stream?

答案 2 :(得分:0)

这不是答案,但您可能会发现此代码更具可读性:

public String ToXml()
{
    var objSerializer =
        new DataContractSerializer(GetType());

    using (var objStream = new MemoryStream())
    {
        //  Serialize the object
        objSerializer.WriteObject(objStream, this);

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

        using (var objReader =
            new StreamReader(objStream))
        {
            // Read Contents into a string
            retirm objReader.ReadToEnd();
        }
    }
}