65.9K
CodeProject is changing. Read more.
Home

Editing Enumerator within foreach loop (well, not really)

Jan 26, 2011

CPOL
viewsIcon

8551

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.