为什么我的 C# Xml 代码仅在我枚举可枚举变量时有效

Why does my C# Xml code only work when I enumerate variable enumerable

我正在编写格式化 XML 文件的代码,以便子文件夹节点实际上嵌套在它们的父节点中。源 XML 将每个文件夹作为根中的一个单独的子节点,而不是像您期望的那样在其主文件夹中包含子文件夹的正确树。这个问题是关于这段代码的:

// Load original XML

string sFile = "PathFile";
XmlDocument doc = new XmlDocument();
doc.Load(sFile);

var n = doc.DocumentElement.SelectNodes ("//*");   // Load all nodes into nodelist n
// int nCount = n.Count;                           // If uncommented code works

foreach(XmlNode x in n)
{ rest of the code }

现在我的代码可以正常工作,但只是有时,即使在 运行 之间没有更改任何内容。我已将其缩小为:当调试 Visual Studio 中的代码时,如果我只是从头到尾 运行 代码,就会出错。如果我中途中断并查看 XmlNodelist n 中的属性(通过将光标悬停在它上面并查看元素计数)它确实有效。 发现这一点后,我添加了

int nCount = n.Count; 

行,现在代码在 运行ning 从头到尾无人监督时有效。

这是怎么回事?解决这个问题的正确方法是什么?注意:doc.LoadXml 不适用于此特定文件。

谢谢大家,

托马斯

简短的回答:因为 XmlNodeList.

的实施有副作用

XmlNode.SelectNodes() returns 一个 XmlNodeList(从技术上讲,一个 XPathNodeList),它是匹配选择的节点的 "live list"(在此例如 XPath 选择)。

当您迭代 XPathNodeList 或以其他方式访问它时,它会通过匹配节点,根据需要构建内部列表,并根据需要 returning 它们。

因此,如果您在遍历节点时尝试重新排列文档,这可能会扰乱迭代并导致它在您遍历所有节点之前停止。当文档在其下方移动时,迭代基本上是在追逐一个移动的目标。

然而,为了 return Count 属性 的值,XPathNodeList 基本上需要找到每个匹配的节点并计算它们,所以它遍历整个匹配集并将它们全部放入内部列表中。

public override int Count {
    get {
        if (! done) {
            ReadUntil(Int32.MaxValue);
        }
        return list.Count;
    }
}

我认为这可以解释您所看到的情况。当您在进行更改之前访问 Count 属性 时,它会构建整个节点列表,作为副作用,因此当您实际遍历它们时仍然会填充该列表。

当然,依赖这种未记录的行为是不明智的。

相反,我建议您实际将 XmlNodeList 的内容复制到您自己的列表中,然后遍历该列表:

string sFile = "PathFile";
XmlDocument doc = new XmlDocument();
doc.Load(sFile);

var allNodes = doc.DocumentElement
    .SelectNodes("//*")
    .OfType<XmlNode>()      // using System.Linq;
    .ToList();

foreach (XmlNode x in allNodes)
{
    // rest of the code 
}