使用 ForEach 枚举时对 IEnumerable 的 C# 修改

发布于 2024-10-21 04:47:30 字数 1079 浏览 1 评论 0原文

这是我正在探索的事情,看看我是否可以将原来的内容

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

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

缩短到不需要 2 个 foreach 循环。

注意 我已经更改了对象的名称以简化此示例(这些来自第 3 方控件)。但为了信息和理解 MdiManager.Pages 继承表单 CollectionBase,而 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 调用创建的列表时,它实际上是对 MdiManager.Pages 集合中的匹配项执行操作,而不是对生成的列表执行操作。

编辑

我想澄清的是,我的问题是如何简化它,我只是想了解为什么当我按照我当前编写的方式执行修改集合时没有问题。

This is something that I was exploring to see if I could take what was

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

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

and shorten it to not require 2 foreach loops.

Note I've changed what the objects are called to simplify this for this example (these come from 3rd party controls). But for information and understanding
MdiManager.Pages inherits form CollectionBase, which in turn inherits IEnumerable

and MdiChild.Close() removes the open child from the MdiManager.Pages Collection, thus altering the collection and causing the enumeration to throw an exception if the collection was modified during enumeration, e.g..

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

I was able to the working double foreach to

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

Why does this not have the same issues dealing with modifying the collection during enumeration? My best guess is that when Enumerating over the List created by the ToList call that it is actually executing the actions on the matching item in the MdiManager.Pages collection and not the generated List.

Edit

I want to make it clear that my question is how can I simplify this, I just wanted to understand why there weren't issues with modifying a collection when I performed it as I have it written currently.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

初相遇 2024-10-28 04:47:30

您对 ToList() 的调用可以拯救您,因为它本质上是重复您上面所做的事情。 ToList() 实际上创建了一个 List(在本例中为 List),其中包含 中的所有元素>MdiManager.Pages,那么您对 ​​ForEach 的后续调用将在列表上运行,而不是在 MdiManager.Pages 上运行。

归根结底,这是风格偏好的问题。我个人并不喜欢 ForEach 函数(我更喜欢 WhereToList() 等查询组合函数,因为它们简单且事实上,它们的设计不会对原始源产生副作用,而 ForEach 则不然)。

您还可以这样做:

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

从根本上讲,所有三种方法都执行完全相同的操作(它们将 MdiManager.Pages 的内容缓存到 List 中,然后迭代缓存的内容列出每个元素并调用 Close()

Your call to ToList() is what saves you here, as it's essentially duplicating what you're doing above. ToList() actually creates a List<T> (a List<MdiChild> in this case) that contains all of the elements in MdiManager.Pages, then your subsequent call to ForEach operates on that list, not on MdiManager.Pages.

In the end, it's a matter of style preference. I'm not personally a fan of the ForEach function (I prefer the query composition functions like Where and ToList() for their simplicity and the fact that they aren't engineered to have side-effects upon the original source, whereas ForEach is not).

You could also do:

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

Fundamentally, all three approaches do exactly the same thing (they cache the contents of MdiManager.Pages into a List<MdiChild>, then iterate over that cached list and call Close() on each element.

最佳男配角 2024-10-28 04:47:30

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

所以本质上这是完成同样事情的另一种方式,只是使用 LINQ。

When you call the ToList() method you're actually enumerating the MdiManager.Pages and creating a List<MdiChild> right there (so that's your foreach loop #1). Then when the ForEach() method executes it will enumerate the List<MdiChild> created previously and execute your action on each item (so that's foreach loop #2).

So essentially it's another way of accomplishing the same thing, just using LINQ.

葬﹪忆之殇 2024-10-28 04:47:30

您也可以将其写为:

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

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

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

You could also write it as:

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

In any case, when you call ToList() extension method on an IEnumerable; you are creating a brand new list. Deleted from its source collection ( in this case, MdiManager.Pages ) will not affect the list output by ToList().

This same technique can be used to delete elements from a source collection without worrying about affecting the source enumerable.

追我者格杀勿论 2024-10-28 04:47:30

你基本上是对的。

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

您也可以执行此操作,这是等效的,并显示您正在执行的操作:

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

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

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

调用中的所有剩余方法、ForEach() 和强制转换都是多余的,可以消除。

You're mostly right.

ToList() creates a copy of the enumeration, and therefore you are enumerating the copy.

You could also do this, which is equivalent, and shows what you are doing:

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

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

Since you are enumerating the elements of the copy enumeration, you don't have to worry about modifying the Pages collection, since each object referece that existed in the Pages collection now also exists in copy and changes to Pages don't affect it.

All the remaining methods on the call, ForEach() and the casts, are superfluous and can be eliminated.

谈情不如逗狗 2024-10-28 04:47:30

乍一看,罪魁祸首是 ToList(),这是一种将项目的副本作为列表返回的方法,从而规避了该问题。

At first glance, the culprit is ToList(), which is a method returning a copy of the items as a List, thus circumventing the problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文