An IEnumerable is not a List, it’s just a promise of a List

It is actually quite obvious, but figuring out the implications still took me a few minutes today.

Suppose you have a simple list of objects:

List<Object> list = new List<Object> { new Object(), new Object() };

And for some reasons you’d like to create a projection from this list using a wrapper class. One reason why you would do this is wrapping business objects into ViewModels.

var enumerable = 	from o in list
					select new Wrapper(o);

The Wrapper class only adds the capability of being flagged to the whole story:

public class Wrapper
{
    public bool IsFlagged { get; set; }
 
    private Object Obj;
    public Wrapper(Object o)
    {
        Obj = o;
    }
}

So what you now have in enumerable is an IEnumerable<Wrapper>. Next, you’d like to use the wrapper functionality and flag the two elements in enumerable:

foreach (var e in enumerable)
{
	e.IsFlagged = true;
}

And then you go forth using your enumerable, only to discover that none of the contained elements are flagged.

What!? Why?

It’s because an IEnumerable is not a List. One needs to understand that prior to calling .ToList() or using foreach() to enumerate over it, no constructor of wrapper has been invoked. Put another way, only because we used this nifty LINQ projection, no Wrapper instances exist yet.

They came into life when we foreach’ed them and disappeared right after that. Whenever we try to use enumerable again, we are enumerating this LINQ expression again. Which means we call the constructors again and we get totally different instances of Wrapper.

For example, suppose we’d use this expression in order to check how many elements are actually selected as an effort to find out why the program doesn’t work as expected:

int numberFlagged = enumerable.Count(e => e.IsFlagged);

This will return 0, as we already know. The best way to find out is to step through the provided code and put a breakpoint into the Wrapper constructor. Please find the whole source code ready for copy & paste at the end of this article.

Although i knew how an IEnumerable behaves, it took me 10 minutes today to find my error in a slightly more complicated example. I guess it’s the special case of using a linq projection together with an IEnumerable that got me.

class Program
{
    static void Main(string[] args)
    {
        List<Object> list = new List<Object> { new Object(), new Object() };
        var enumerable = from o in list
                            select new Wrapper(o);
        foreach (var e in enumerable)
        {
            e.IsFlagged = true;
        }
        int numberFlagged = enumerable.Count(e => e.IsFlagged);
    }
    public class Wrapper
    {
        public bool IsFlagged { get; set; }
        private Object Obj;
        public Wrapper(Object o)
        {
            Obj = o;
        }
    }
}
Advertisements