为啥我的 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(); @jdwengXmlNodeList
没有.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<XmlNode>
将被声明。
@jdweng 我使用 C# 已经 12 年了,我对使用 var
非常满意。你真的认为你会改变某人的编码风格,甚至不为你的建议提供任何理由吗?
这是一个完美的答案,我希望谢谢你!很清楚出了什么问题。我尝试了您的解决方案并且它有效,很高兴不使用您所说的无证行为。深入了解您的答案,XmlNodeList 是实时的这一事实是否也意味着对该列表中 XmlNodes 所做的更改会自动反映在源 XmlDocument 中?因为我花了很多时间构建动态 xPath,以将 foreach 循环中所做的更改反映回 XmlDocument。如果我最终不能使用实时列表,那就最好了。
如果我可以指定类型时使用“var”,我知道我的大学老师会给我的分数。以上是关于为啥我的 C# Xml 代码仅在我枚举变量 enumerable 时才有效的主要内容,如果未能解决你的问题,请参考以下文章
仅在 C# 中的 Date 类型 - 为啥没有 Date 类型?
为啥我的变量仅在 Python 中的某个函数/语句中发生变化?
为啥我的 UICollectionView 支持方法仅在我的 ViewController 设置为初始视图控制器时调用?