C#使用ForEach进行枚举时修改IEnumerable

时间:2011-03-10 20:21:16

标签: c# .net enumeration

这是我正在探索的事情,看看我是否可以采取

List<MdiChild> openMdiChildren = new List<MdiChild>();
foreach(child in MdiManager.Pages)
{
    openMdiChildren.Add(child);
}

foreach(child in openMdiChild)
{
   child.Close();
}

并将其缩短为不需要2 foreach个循环。

注意我已经更改了调用对象的内容以简化此示例(这些来自第三方控件)。但是为了获得信息和理解 MdiManager.Pages继承了CollectionBase格式,后者继承IEnumerable

MdiChild.Close()MdiManager.Pages集合中删除打开的子节点,从而改变集合并导致枚举在枚举期间修改集合时抛出异常,例如..

foreach(child in MdiManage.Pages)
{
   child.Close();
}

我能够工作加倍foreach

((IEnumerable) MdiManager.Pages).Cast<MdiChild>.ToList()
.ForEach(new Action<MdiChild>(c => c.Close());

为什么在枚举期间没有同样的问题来处理修改集合?我最好的猜测是,当对ToList调用创建的List进行枚举时,它实际上正在对MdiManager.Pages集合中的匹配项执行操作,而不是生成的List。

修改

我想说清楚我的问题是如何简化这一点,我只是想了解为什么在我执行它时修改集合时没有问题,因为我现在已经编写了它。

5 个答案:

答案 0 :(得分:8)

您拨打ToList()的电话可以帮您解决这个问题,因为它基本上会复制您在上面所做的事情。 ToList()实际上创建了List<T>(在这种情况下为List<MdiChild>),其中包含MdiManager.Pages中的所有元素,然后您对ForEach的后续调用将在< em>那个列表,而不是MdiManager.Pages

最后,这是风格偏好的问题。我个人并不是ForEach函数的粉丝(我更喜欢WhereToList()这样的查询组合函数,因为它们非常简单,而且它们没有被设计为具有副作用的事实 - 对原始来源的影响,而ForEach则不是。

你也可以这样做:

foreach(child in MdiManager.Pages.Cast<MdiChild>().ToList())
{
    child.Close();
}

从根本上说,这三种方法完全相同(它们将MdiManager.Pages的内容缓存到List<MdiChild>中,然后遍历该缓存列表并在每个元素上调用Close()。 / p>

答案 1 :(得分:0)

你大部分都是对的。

ToList()创建枚举的副本,因此您枚举副本。

您也可以这样做,这是等效的,并显示您正在做的事情:

var copy = new List<MdiChild>(MdiManager.Pages.Cast<MdiChild>());

foreach(var child in copy)
{
    child.Close();
}

由于您要枚举copy枚举的元素,因此您不必担心修改Pages集合,因为现在每个对象都引用Pages集合中存在的对象也存在于copy中,对Pages的更改不会影响它。

通话中的所有剩余方法ForEach()和演员表都是多余的,可以消除。

答案 2 :(得分:0)

乍一看,罪魁祸首是ToList(),这是一个方法以项目的形式返回项目的副本,从而避免了这个问题。

答案 3 :(得分:0)

当您调用ToList()方法时,实际上您正在枚举MdiManager.Pages并在那里创建List<MdiChild>(这就是您的foreach循环#1)。然后,当ForEach()方法执行时,它将枚举先前创建的List<MdiChild>并对每个项执行操作(以便foreach循环#2)。

所以基本上它只是使用LINQ实现同样的事情的另一种方式。

答案 4 :(得分:0)

你也可以把它写成:

foreach(var page in MdiManager.Pages.Cast<MdiChild>.ToList())
    page.Close();

在任何情况下,当你在IEnumerable上调用ToList()扩展方法时;你正在创建一个全新的列表。从其源集合中删除(在本例中为MdiManager.Pages)不会影响ToList()的列表输出。

可以使用相同的技术从源集合中删除元素,而不必担心影响源可枚举。