嵌套使用语句和微软代码分析

25

最近我开启了额外的代码分析规则。让我惊讶的是,在我一直认为是最佳实践的地方出现了违规情况。如果我有两个嵌套的可清理对象,我会像这样放置两个using语句:

    using (StringReader strReader = new StringReader(xmlString))
    using (XmlReader xmlReader = XmlReader.Create(strReader))
    {
        result.ReadXml(xmlReader);
    }

这也对应于高评分的Q&A C#中嵌套使用语句

我收到的违规声明如下:

Warning 18  CA2202 : Microsoft.Usage : Object 'strReader' can be disposed more
than once in method '????'. To avoid generating a System.ObjectDisposedException
you should not call Dispose more than one time on an object.: Lines: ??

我所做的是一种直觉上的尝试和错误,认为关闭外部流也可能会处理内部流。我快速修复了我的代码,就像这样:

    using (XmlReader xmlReader = XmlReader.Create(new StringReader(xmlString)))
    {
        result.ReadXml(xmlReader);
    }

哎呀!警告消息已经消失了。但是,塔达!出现了一个新的警告:

Warning 18  CA2000 : Microsoft.Reliability : In method '????????', object 
'new StringReader(xmlString)' is not disposed along all exception paths. Call
System.IDisposable.Dispose on object 'new StringReader(xmlString)' before all 
references to it are out of scope.

然后我找到了一个非常丑陋的解决方案:

    {
        StringReader strReader = null;
        try
        {
            strReader = new StringReader(xmlString);
            using (XmlReader xmlReader = XmlReader.Create(strReader))
            {
                strReader = null;
                result.ReadXml(xmlReader);
            }
        }
        finally
        {
            if (strReader != null) strReader.Dispose();
        }
    }

作为最后一步(像每个好程序员一样),我查看了CA2202帮助页面,出乎意料的是,恰好我的最后一个丑陋的解决方案被提出来解决了这个问题?

在使用clutters时添加try{} finally会使代码非常混乱!对于我来说,嵌套的using更易读。

问题:有更好的方法吗?我正在寻找一种直观可理解的解决方案。每个看到这个片段的人都会好奇发生了什么。

提前感谢您的答案。


像您在PS中提到的问题可能属于MetaStackoverflow。 实际上,该问题可能已经在那里被提出并得到回答。 - Matt Fenwick
顺便说一下,我也尝试了以下代码:using (StringReader strReader = new StringReader(xmlString)) using (XmlReader xmlReader = XmlReader.Create(strReader)) { strReader = null; result.ReadXml(xmlReader); }导致编译错误 Error 18 Cannot assign to 'strReader' because it is a 'using variable'。 - George Mamaladze
2个回答

21
问题不在于嵌套的使用。它们是很好的并且通常被推荐的。问题在于,如果你传递一个具有 CloseInput == true 的 XmlReaderSettings,则 XmlReader 会释放 TextReader,但 CA2202 规则并不聪明,不能理解你的代码不会进入该分支。保持你的嵌套使用,并将 CA2202 违规视为误报。
如果你想要显式地提高代码的可读性和/或可维护性,请使用一个具有 CloseInput 设置为 false 的 XmlReaderSettings,但这是默认值,因此不是必需的,并且需要明确的是,这不会满足该规则。
顺便说一下,各种流和读取器类型存在着类似于 CA2202 的问题场景。不幸的是,它们并不都像这个一样,因此最佳的处理方式可能因引起问题的类型而不同。

xmlReaderSettings.CloseInput = false 不符合规则。抑制似乎是处理此问题的唯一正确方式。通常容忍在 Dispose 实现中进行双重释放似乎是个好主意,因此某些用户可能会在静默中释放您,如果有人将您的类包装在 using 语句中,则会出现问题!感谢您的精彩解释。已接受! - George Mamaladze
4
我并不是在建议将 CloseInput 设为 false 就能符合规则,而是建议你在代码中更加明确以提高其可读性和可维护性。至于可以容忍多次处理,虽然没有相应的 FxCop 规则,但 .NET 有关于这方面的设计指南。 - Nicole Calinoiu

0

最近我遇到了类似的问题,但是由于我正在使用序列化器,所以必须对其进行调整,因为我无法立即将stringWriter设置为null。这种解决方法避免了所有CA警告:

StringWriter stringWriter = null;
XmlWriter xmlWriter = null;
string serializedValue = null;

try
{
    XmlSerializer xmlserializer = new XmlSerializer(typeof(T));
    stringWriter = new StringWriter();

    xmlWriter = XmlWriter.Create(stringWriter);
    xmlserializer.Serialize(xmlWriter, value);
    xmlWriter.Flush();
    serializedValue = stringWriter.ToString();
}
finally
{
    if (xmlWriter != null) //Both objects need disposed 
    {
        xmlWriter.Dispose(); //stringWriter will dispose automatically too
    }
    else if (stringWriter != null) //XmlWriter failed to create
    {
        stringWriter.Dispose(); //just dispose stringWriter
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接