C# LINQ 对同一数据的多重评估

C# LINQ Multiple evaluation of the same data

给定一个 class 保存(并初始化一次)一个 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);
    }
}

为什么每次需要 myClass.Values 的评估值的用法都要重新评估它们?

例如:

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);
    }
}

这将打印 20 次“Convert:”和“Value:”。

我知道将值保存为列表(例如 ToList())可以解决这个问题,但据我了解,一旦对值进行评估,将从内存中使用这些值(就像我使用Lazy<T> 然后是 Values.value),为什么这不是同一种情况?

这是 IEnumerable<T> 的正常行为。当在 foreach 循环中调用每个项目时,实际上会为集合中的每个项目调用投影调用 Select(但显然你只调用了一次)。

因此,当您使用 foreach 循环时,IEnumerable 对象正在调用枚举器并获取下一个项目(如果集合中有可用的东西,类似于状态机)。

当在 foreach 循环中请求下一个项目时,它会跟踪正在枚举的当前项目,它会再次调用,从而导致再次调用 Convert 方法。