意外的Foreach行为C#

时间:2017-01-04 17:05:49

标签: c# xml linq loops foreach

当条件为foreach loop实际上在true内的所有指令之后,if statement有一个意外的行为,循环中断。我试着评论if语句并且一切正常(将所有元素迭代到ienumerable中)。有人能解释我为什么吗?

var allRef = projDefinition
    .Element(msbuild + "Project")
    .Elements(msbuild + "ItemGroup")
    .Elements(msbuild + "COMReference")
    .Where(refElem => find.Any(f => refElem.FirstAttribute.Value.ToLower().Contains(f)))
    .Select(refElem => refElem);


foreach (var xElement in allRef)
{
    var name = xElement.FirstAttribute.Value;
    var dllPath = dllFiles.FirstOrDefault(dll => dll.ToLower().Contains(name.ToLower()));

    if (dllPath != null)
    {
        var dllName = dllPath.Substring(dllPath.LastIndexOf('\\') + 1, dllPath.LastIndexOf('.') - dllPath.LastIndexOf('\\') - 1);
        xElement.Remove();
        XElement elem = new XElement(msbuild + "Reference", new XAttribute("Include", dllName));
        elem.Add(new XElement(msbuild + "HintPath", HintPath.GetHintPath(dllPath)));
        elem.Add(new XElement(msbuild + "EmbedInteropTypes", "False"));
        projDefinition.Root.Elements(msbuild + "ItemGroup").ElementAt(0).Add(elem);
    }

}

projDefinition.Save(fullProjectPath);

2 个答案:

答案 0 :(得分:5)

你在allRef上循环,同时你用xElement.Removeforeach移除它,这与循环迭代器混淆。

Basicaly IEnumerator创建.ToList()并使用它从当前元素移动到下一个元素。通过从集合中删除元素,同时仍然覆盖它,你会打扰它。

它不会导致异常,但是你的循环会导致意外的行为,比如不会遍历所有元素。

您可能需要阅读how does foreach works

针对此类问题的简单解决方案是添加foreach(正如@Ivan Stoev在评论中所述)。它起作用是因为它创建了新列表,因此allRef迭代器被创建到新列表而不是allRef。这意味着您可以安全地从SELECT appID, hours FROM games ORDER BY hours DESC LIMIT 4 UNION SELECT appID, hours FROM games WHERE appID = 221380 中删除元素。

答案 1 :(得分:2)

有关具体如何工作的更多细节,@factory.post_generation基本上类似于节点的“链表”。每个元素都有对其兄弟及其父元素的引用。

各种查询(如XDocument)都使用迭代器块并且被懒惰地评估。您可以在reference source

中进行此操作

在当前循环变量上调用Elements后,该元素将从文档树中删除。它对父母及其兄弟的引用现在都是Remove。当迭代器在null循环的MoveNext调用中恢复时,无处可去;迭代完成。

正如评论&其他答案,在迭代之前简单调用foreach将解决问题,因为它将遍历您的查询并将其缓存在新列表中。稍后从文档中删除节点不会影响列表的内容。