异常过滤器触发CA2202?

3

我有一个函数,简化后看起来像这样:

public static string Merge(string xml1, string xml2)
{
   try
   {
      var doc1 = XDocument.Load(new StringReader(xml1));
      var doc2 = XDocument.Load(new StringReader(xml2));

      // these "die" by throwing InvalidOperationException
      var root1 = GetElementOrDie(doc1, Names.RootElementName);
      var root2 = GetElementOrDie(doc2, Names.RootElementName);

      foreach (var element in root2.Elements())
         root1.Add(element);

      return doc1.ToString();
   }
   catch (Exception e) when (!(e is InvalidOperationException))
   {
      throw new InvalidOperationException(e.Message, e);
   }
}

在 Visual Studio 2015 下,这会生成代码分析警告 CA2202:
对象 'root2.Elements().GetEnumerator()' 在方法 'XmlProcessor.Merge(string, string)' 中可能被处理多次。为避免生成 System.ObjectDisposedException,您不应在一个对象上调用 Dispose 多次。
如果我删除 when 子句,则警告消失。
这是怎么回事?警告是否正确?
private static XElement GetElementOrDie(XContainer container, XName elementName)
{
   var element = container.Element(elementName);
   if (element == null)
      throw new InvalidOperationException();
   return element;
}

2
这似乎更像是代码分析问题。我宁愿忽略这个警告。 - Habib
我也看到过 CA 错误地抱怨在使用空条件运算符时没有处理对象的释放,例如 disposable?.Dispose(); - juharr
1个回答

1

我认为这是与代码分析有关的问题,我会将其抑制。如果我将循环从:

foreach (var element in root2.Elements())
   root1.Add(element);

将其更改为这样以明确暴露它所抱怨的枚举器:
var enumerator = root2.Elements().GetEnumerator();
while (enumerator.MoveNext())
{
    XElement item = enumerator.Current;
    root1.Add(item);
}

然后,即使在异常上使用了when筛选器,也不再抛出CA2202


我认为你是对的,但这个更改不再调用枚举器上的Dispose,因此停止警告并不令人惊讶。我认为要明确回答这个问题需要检查反汇编并确认代码中没有任何路径会导致Dispose被调用多次。在LinqPad中进行快速检查只生成了一个调用,但我不是足够熟练的IL大师,无法确定是否存在某些跳转或循环会导致该指令被执行多次。 - dlf
话虽如此,但第二个版本中我唯一遇到的“CA2000:在失去作用域之前处理对象”错误仍然是在您的StringReader上。 - NikolaiDante

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