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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With