Click here to Skip to main content
15,867,686 members
Articles / Programming Languages / C#

IEnumerable: Lazy and Dangerous

Rate me:
Please Sign up or sign in to vote.
3.88/5 (16 votes)
12 Jul 2014Apache2 min read 47.1K   11   23
IEnumerable: Lazy and Dangerous

Time and time again, I am burnt by the same bug in my code.

There are two kinds of enumerables in .NET: enumerators and generators. Enumerators go over some collection and return its items. Enumerator will always return the same values, no matter how many times you iterate over it. This only changes when someone adds or removes items from the collection.

Generators, on the other hand, compute new values every time. They may or may not be the same, depending on how the generator is built and what happened around it. All those neat LINQ methods like “where” and “select” are in fact generators.

For example,

C#
IEnumerable<string> names = new[] { "Paris", "Berlin", "London" }; // enumerator
IEnumerable<City> cities = names.Select(name=>new City(name)); // generator

Every time you do foreach (var city in cities), a different set of cities is produced. E.g.

C#
foreach (var city in cities) city.Visited = true;
...
foreach (var city in cities)
{
    Console.WriteLine(city.Visited); // prints false
}

One can easily convert a generator to an enumerator by adding ToList(), ToArray(), or ToDictionary(). This will create a “real” data structure that is not modified.

C#
var cityList = names.Select(name=>new City(name)).ToList();
foreach (var city in cityList) city.Visited = true;
...
foreach (var city in cityList)
{
    Console.WriteLine(city.Visited); // prints true
}

Even more subtle issues can occur when using conditions:

C#
cityList[0].Visited = true;
var visitedCities = cityList.Where(city=>city.Visited);
Console.WriteLine( visitedCities.Count() ); // 1
...
cityList[0].Visited = false;
Console.WriteLine( visitedCities.Count() ); // 0

This is an obvious case of hidden dependency. We modified something inside cityList, but it “magically” affects similarly unrelated visitedCities. Hidden dependencies are evil, because they are, well, hidden, and people tend to forget about them.

The most annoying part is that generators and enumerators look exactly the same, and it may be quite difficult, or even impossible to tell whether particular IEnumerable is a generator or an enumerator. Functional style programming assumes read-only objects, so it does not matter, but throwing in any modifiable state creates a dangerous mix.

It does not mean one should not use IEnumerable in stateful scenarios, but it is better to be careful, you have been warned!

PS From the Future (2014). Resharper has a warning for multiple enumerations of an IEnumerable. If you pay attention to the warning, your chances of been burnt by this bug will be greatly reduced. Unfortunately, Resharper costs money, and to the best of my knowledge out-of-the-box versions of Visual Studio/C# compiler do not have such warning.

This article was originally posted at http://www.ikriv.com/blog?p=792

License

This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0


Written By
Technical Lead Thomson Reuters
United States United States
Ivan is a hands-on software architect/technical lead working for Thomson Reuters in the New York City area. At present I am mostly building complex multi-threaded WPF application for the financial sector, but I am also interested in cloud computing, web development, mobile development, etc.

Please visit my web site: www.ikriv.com.

Comments and Discussions

 
QuestionIs there missing City class? Pin
DarkStarHarry28-Apr-15 9:48
DarkStarHarry28-Apr-15 9:48 
GeneralMy vote of 5 Pin
mr. Duan14-Jul-14 16:51
professionalmr. Duan14-Jul-14 16:51 
GeneralMy vote of 5 Pin
DarkStarHarry14-Jul-14 3:53
DarkStarHarry14-Jul-14 3:53 
GeneralMy vote of 5 Pin
Oleg A.Lukin13-Jul-14 19:44
Oleg A.Lukin13-Jul-14 19:44 
GeneralMy vote of 1 Pin
Marc Clifton13-Jul-14 3:17
mvaMarc Clifton13-Jul-14 3:17 
GeneralMy vote of 5 Pin
CatchExAs12-Jul-14 6:12
professionalCatchExAs12-Jul-14 6:12 
GeneralRe: My vote of 5 Pin
Ivan Krivyakov12-Jul-14 15:30
Ivan Krivyakov12-Jul-14 15:30 
GeneralMy vote of 1 Pin
Stephen M Russell12-Jul-14 3:30
Stephen M Russell12-Jul-14 3:30 
GeneralRe: My vote of 1 Pin
Ivan Krivyakov12-Jul-14 4:27
Ivan Krivyakov12-Jul-14 4:27 
There is no bug. There is a gotcha. And I am not attacking anyone, it's all in your head, dude.
GeneralMy vote of 2 Pin
Stephen M Russell11-Jul-14 22:56
Stephen M Russell11-Jul-14 22:56 
GeneralRe: My vote of 2 Pin
Ivan Krivyakov12-Jul-14 4:35
Ivan Krivyakov12-Jul-14 4:35 
Question[My vote of 2] Lots of things incorrect with no guidance/solution Pin
William E. Kempf11-Jul-14 4:35
William E. Kempf11-Jul-14 4:35 
AnswerRe: [My vote of 2] Lots of things incorrect with no guidance/solution Pin
Ivan Krivyakov11-Jul-14 12:52
Ivan Krivyakov11-Jul-14 12:52 
GeneralMy vote of 5 Pin
Klaus Luedenscheidt10-Jul-14 18:46
Klaus Luedenscheidt10-Jul-14 18:46 
GeneralRe: My vote of 5 Pin
Ivan Krivyakov12-Jul-14 15:33
Ivan Krivyakov12-Jul-14 15:33 
GeneralMy vote of 4 Pin
_Noctis_10-Jul-14 16:18
professional_Noctis_10-Jul-14 16:18 
GeneralRe: My vote of 4 Pin
Ivan Krivyakov10-Jul-14 16:22
Ivan Krivyakov10-Jul-14 16:22 
GeneralRe: My vote of 4 Pin
_Noctis_10-Jul-14 17:57
professional_Noctis_10-Jul-14 17:57 
GeneralRe: My vote of 4 Pin
_Noctis_12-Jul-14 14:35
professional_Noctis_12-Jul-14 14:35 
GeneralRe: My vote of 4 Pin
Ivan Krivyakov12-Jul-14 15:33
Ivan Krivyakov12-Jul-14 15:33 
GeneralRe: My vote of 4 Pin
_Noctis_12-Jul-14 18:39
professional_Noctis_12-Jul-14 18:39 
GeneralMy vote of 5 Pin
Jay R. Wren15-Feb-11 2:16
Jay R. Wren15-Feb-11 2:16 
GeneralIEnumerable semantics Pin
supercat99-Feb-11 8:23
supercat99-Feb-11 8:23 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.