My previous post discussing using use of Enumerable.Select to map collections fails to cover some potentially unexpected behaviour you may encounter in practice. I noticed this when unit testing some code recently where my tests were failing due to violated expectations.

The tests in question mock the mapper and set expectations as to how it will be called. The test was failing because the mapper was being invoked more than expected. This is due to the deferred execution behaviour that Enumerable.Select and other such methods exhibit. This effectively means that the majority of the select logic is not executed until you start using the returned enumerator. This is advantageous for performance, especially if you do not wish to look at the entire result set. However depending on your usage pattern it is possible that this behaviour will result in significant extra work being performed. In addition to potential performance issues this could result in bugs due to actions being performed more times than expected.

The cause of my issues was repeated use of the result of the select such that multiple enumerators were obtained. Each enumerator would start at the beginning of the source enumeration. This may happen due to something as simple as using Enumerable.Count to obtain the size of the enumeration.

This is obviously undesirable. There are two primary ways which I am aware of to address this. Firstly you could rewrite your code to use a single enumerator. This may not be feasible and could lead to code lacking proper cohesion. The other alternative is to use Enumerable.ToList. This immediately produces a List<T> from the enumerator. This may then be accessed repeatedly without having the elements regenerated. The disadvantage here is that you lose all the benefits of deferred execution. The entire source enumeration will be processed even if you only need a few elements.

The example from my previous post used Enumerable.ToList so that the result could be assigned to a list property and such does not have this issue. The problem was encountered when mapping the other way and using the result in a more sophisticated fashion. I would not have noticed this has I been able to stub the mapper as I initially intended which is an unexpected benefit from being stuck at the time with an older version of the mocking framework being used.