这是我正在探索的事情,看看我是否可以采取
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。
修改
我想说清楚我的问题是如何简化这一点,我只是想了解为什么在我执行它时修改集合时没有问题,因为我现在已经编写了它。
答案 0 :(得分:8)
您拨打ToList()
的电话可以帮您解决这个问题,因为它基本上会复制您在上面所做的事情。 ToList()
实际上创建了List<T>
(在这种情况下为List<MdiChild>
),其中包含MdiManager.Pages
中的所有元素,然后您对ForEach
的后续调用将在< em>那个列表,而不是MdiManager.Pages
。
最后,这是风格偏好的问题。我个人并不是ForEach
函数的粉丝(我更喜欢Where
和ToList()
这样的查询组合函数,因为它们非常简单,而且它们没有被设计为具有副作用的事实 - 对原始来源的影响,而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()的列表输出。
可以使用相同的技术从源集合中删除元素,而不必担心影响源可枚举。