为啥我的 C# Xml 代码仅在我枚举变量 enumerable 时才有效

Posted

技术标签:

【中文标题】为啥我的 C# Xml 代码仅在我枚举变量 enumerable 时才有效【英文标题】:Why does my C# Xml code only work when I enumerate variable enumerable为什么我的 C# Xml 代码仅在我枚举变量 enumerable 时才有效 【发布时间】:2020-10-06 07:27:24 【问题描述】:

我正在编写格式化 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 不适用于此特定文件。

谢谢你,

托马斯

【问题讨论】:

以下工作有效吗? : XmlNode[] n = doc.DocumentElement.SelectNodes("//*").ToArray(); @jdweng XmlNodeList 没有.ToArray() 方法,也不能在其上调用Linq .ToArray() 扩展方法,因为它没有实现IEnumerable<T> 【参考方案1】:

简短的回答:因为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 

【讨论】:

我的建议。除非必须,否则不要使用 'var'。 在这里var 的使用做得很好。从代码中可以清楚地看出,List&lt;XmlNode&gt; 将被声明。 @jdweng 我使用 C# 已经 12 年了,我对使用 var 非常满意。你真的认为你会改变某人的编码风格,甚至不为你的建议提供任何理由吗? 这是一个完美的答案,我希望谢谢你!很清楚出了什么问题。我尝试了您的解决方案并且它有效,很高兴不使用您所说的无证行为。深入了解您的答案,XmlNodeList 是实时的这一事实是否也意味着对该列表中 XmlNodes 所做的更改会自动反映在源 XmlDocument 中?因为我花了很多时间构建动态 xPath,以将 foreach 循环中所做的更改反映回 XmlDocument。如果我最终不能使用实时列表,那就最好了。 如果我可以指定类型时使用“var”,我知道我的大学老师会给我的分数。

以上是关于为啥我的 C# Xml 代码仅在我枚举变量 enumerable 时才有效的主要内容,如果未能解决你的问题,请参考以下文章

仅在 C# 中的 Date 类型 - 为啥没有 Date 类型?

为啥我的变量仅在 Python 中的某个函数/语句中发生变化?

为啥我的 UICollectionView 支持方法仅在我的 ViewController 设置为初始视图控制器时调用?

Unity:为啥添加事件侦听器仅在唤醒功能中起作用?

为啥提交按钮仅在我的示例中按下两次时才起作用?

为啥我的领域通知令牌仅在我的活动设备上触发,而不是在所有具有开放领域的设备上触发?