Geo*_*ett 9 c# linq exception deferred-execution
请考虑以下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(@"<Parts>
<Part name=""DisappearsOk"" disabled=""true""></Part>
<Part name=""KeepMe"" disabled=""false""></Part>
<Part name=""KeepMe2"" ></Part>
<Part name=""ShouldBeGone"" disabled=""true""></Part>
</Parts>");
XmlNode root = xmlDoc.DocumentElement;
List<XmlNode> disabledNodes = new List<XmlNode>();
try
{
foreach (XmlNode node in root.ChildNodes.Cast<XmlNode>()
.Where(child => child.Attributes["disabled"] != null &&
Convert.ToBoolean(child.Attributes["disabled"].Value)))
{
Console.WriteLine("Removing:");
Console.WriteLine(XDocument.Parse(node.OuterXml).ToString());
root.RemoveChild(node);
}
}
catch (Exception Ex)
{
Console.WriteLine("Exception, as expected");
}
Console.WriteLine();
Console.WriteLine(XDocument.Parse(root.OuterXml).ToString());
Console.ReadKey();
}
}
}
Run Code Online (Sandbox Code Playgroud)
当我在visual studio express 2010中运行此代码时,我没有像预期的那样得到异常.我期待一个,因为我在迭代它时从列表中删除了一些东西.
我得到的是一个列表,只删除了第一个子节点:

为什么我没有得到无效的操作异常?
请注意,IDEOne.com中的等效代码确实提供了预期的异常:http://ideone.com/qoRBbb
另请注意,如果我删除所有LINQ(.Cast().Where()),我得到相同的结果,只删除一个节点,没有异常.
我的VSExpress设置有问题吗?
请注意,我知道延迟执行是涉及的,但我希望在迭代时使用where子句迭代源枚举(子注释),这将给出我期望的异常.
我的问题是我在VSexpress中没有得到那个例外,但是在IDEOne中做了(我希望在两个/所有情况下,或者至少如果没有,我希望得到正确的结果).
从Wouter的回答来看,当第一个孩子被移除时,它似乎使迭代器无效,而不是给出异常.有什么官方说的吗?在其他情况下会出现这种行为吗?我会默认无效地使迭代器失效,而不是"无声但致命"的异常.
即使下面的代码也不会抛出任何异常:
foreach (XmlNode node in root.ChildNodes)
root.RemoveChild(node);
Run Code Online (Sandbox Code Playgroud)
并且它将恰好删除一个元素。我不是100%我的解释是正确的,但它是在正确的轨道上。当您迭代集合时,您将检索其枚举器。对于作为集合的 XmlNode,这是一个名为 的自定义类XmlChildEnumerator。
如果您通过 Reflector 查找 MoveNext 实现,您会发现枚举器记住了它当前正在查看的节点。当您调用 MoveNext 时,您将移动到下一个同级。
上面的代码中发生的事情是您从集合中获取第一个节点。在 foreach 循环体中隐式生成的枚举器将第一个节点作为其当前节点。然后,在 foreach 循环体中删除该节点。
现在该节点已从列表中分离,并且执行将再次调用 MoveNext。但是,由于我们刚刚从集合中删除了第一个节点,因此它已从集合中分离,并且节点没有兄弟节点。由于该节点没有兄弟节点,因此迭代停止并退出 foreach 循环,从而仅删除单个元素。
这不会抛出异常,因为它不检查集合是否已更改,它只是想移动到它可以找到的下一个节点。但由于删除(分离)的节点不属于集合,因此循环停止。
希望这能解决这个问题。