2

我正在处理格式化 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; 

行,现在代码从头到尾在无人监督的情况下运行。

这里发生了什么,解决这个问题的正确方法是什么?注意: doc.LoadXml 不适用于此特定文件。

谢谢你的负荷,

托马斯

4

1 回答 1

5

简短的回答:因为XmlNodeList.

XmlNode.SelectNodes()返回一个XmlNodeList(从技术上讲,一个XPathNodeList),它是与选择匹配的节点的“活动列表”(在本例中为 XPath 选择)。

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

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

但是,为了返回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 
}
于 2020-06-16T16:15:54.040 回答