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
方法。
给定一个 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
方法。