Search code examples
c#linqlazy-evaluation

C# LINQ Multiple evaluation of the same data


Given a class that holds (and initializes once) an IEnumerable<T>:

public class MyClass
{
    public MyClass()
    {
        Values = Sequence(0, 10).Select(i => Convert(i));
    }

    public IEnumerable<string> Values { get; private set; }

    private static string Convert(int i)
    {
        string iStr = i.ToString();
        Console.WriteLine("Convert: " + iStr);
        return iStr;
    }

    private static IEnumerable<int> Sequence(int start, int end)
    {
        return Enumerable.Range(start, end - start + 1);
    }
}

Why does every usage that requires the evaluated values of myClass.Values re-evaluates them?

e.g.:

static void Main(string[] args)
{
    MyClass myClass = new MyClass();

    foreach (var v in myClass.Values)
    {
        Console.WriteLine(v);
    }
    foreach (var v in myClass.Values)
    {
        Console.WriteLine("Value: " + v);
    }
}

This will print 20 times "Convert: " and "Value: ".

I know that saving the Values as a list (with ToList() for example) will solve this, but it was my understanding that once Values are evaluated the values will be used from memory (as if I used Lazy<T> and then Values.value), why is this not the same case?


Solution

  • This is the normal behavior of IEnumerable<T>. Your projection call Select is actually invoked for every item in the collection (but apparently you only called it once)when each item is called in the foreach loop.

    So when you are looping with foreach loop the IEnumerable object is calling the enumerator and getting the next item if any available in the collection something similar to state machine.

    It keeps track of the current item being enumerated when next item is request in the foreach loop it is called again so resulting the Convert method to be called again.