Editing Enumerator within foreach loop (well, not really)






4.93/5 (4 votes)
A simple way to edit a list or collection which is iterated in a foreach loop
Recently, I had to process a
Collection
of class objects after a split over some properties. I did the split through a relatively simple Select
and Distinct
extension methods. The next task was to split the result again in batches and process. I discovered this while doing that. The ToList
extension method, supposedly, creates a deep copy of the IEnumerator
returned by the LINQ expression. Following is a simple code to demonstrate.
class Animals { public bool Herbivore { get; set; } public bool IsMammal { get; set; } public string CommonName { get; set; } } private static void SendBatches() { Collection<Animals> animalCollection = new Collection<Animals>(); animalCollection.Add(new Animals { Herbivore = true, IsMammal = true, CommonName = "Cow" }); animalCollection.Add(new Animals { Herbivore = false, IsMammal = true, CommonName = "Cat" }); animalCollection.Add(new Animals { Herbivore = false, IsMammal = true, CommonName = "Dog" }); animalCollection.Add(new Animals { Herbivore = true, IsMammal = true, CommonName = "Lamb" }); // Ignoring those weird mammals who lay eggs var herbivores = (from animal in animalCollection where animal.Herbivore select new { Name = animal.CommonName, LaysEggs = !animal.IsMammal }); foreach (var herbivore in herbivores) { if (herbivore.Name.StartsWith("C", StringComparison.InvariantCultureIgnoreCase)) { animalCollection.Remove(animalCollection.Where(x => string.Compare(x.CommonName, herbivore.Name, true) == 0).First()); } } }As expected, the
foreach
will fail at the second iteration since we have changed the Enumerator
. But, if we change the LINQ expression to the following, it works:
var herbivores = (from animal in animalCollection
where animal.Herbivore
select new { Name = animal.CommonName, LaysEggs = !animal.IsMammal }).ToList();
Even ToArray
gives the same behavior.