C#LINQ对相同数据的多重评估
给定一个包含(并初始化一次)IEnumarable的类: 这将打印20次“转换:”和“值:”C#LINQ对相同数据的多重评估,c#,linq,lazy-evaluation,C#,Linq,Lazy Evaluation,给定一个包含(并初始化一次)IEnumarable的类: 这将打印20次“转换:”和“值:” 我知道将值保存为列表(例如使用ToList())可以解决这一问题,但我的理解是,一旦对值进行了计算,这些值将从内存中使用(就像我使用了Lazy,然后使用Values.value),为什么情况不一样?这是IEnumerable的正常行为。在foreach循环中调用每个项时,实际上会为集合中的每个项调用投影调用Select(但显然只调用了一次) 因此,当您使用foreach循环时,IEnumerable对象
我知道将值保存为列表(例如使用
ToList()
)可以解决这一问题,但我的理解是,一旦对值进行了计算,这些值将从内存中使用(就像我使用了Lazy
,然后使用Values.value
),为什么情况不一样?这是IEnumerable
的正常行为。在foreach
循环中调用每个项时,实际上会为集合中的每个项调用投影调用Select
(但显然只调用了一次)
因此,当您使用foreach
循环时,IEnumerable
对象正在调用枚举器,并获取集合中的下一项(如果有可用项),类似于状态机
当foreach循环中的下一个项被请求时,它会跟踪被枚举的当前项,并再次调用它,从而导致再次调用Convert
方法。将值具体化为一个集合(例如,放入列表
):
公共类MyClass
{
私有列表m_值;
...
公共IEnumerable值{
得到{
//而不是枚举和计算项目
//我们返回计算项的集合
返回m_值;
}
专用设备{
//现在,值将被枚举一次,并且项
//将存储在m_值集合中
m_Values=value.ToList();
}
}
...
}
如果要以惰性方式执行此操作,请执行以下操作:
公共类MyClass
{
私人清单m_价值清单;
私有IEnumerable m_值;
...
公共IEnumerable值{
得到{
//如果缓存不存在,则具体化
如果(m_ValuesList==null)
m_ValuesList=m_Values.ToList();
//而不是枚举和计算项目
//我们返回计算项的集合
返回m_值列表;
}
专用设备{
m_值=值;
//丢弃缓存(物化集合)
m_ValuesList=null;
}
}
...
}
具体化(.ToArray()
,.ToList()
),即将结果存储在集合中,然后计算一次。看看这个:我想问一下在这种情况下使用LINQ有什么好处,但我看到了关于
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);
}
}
public class MyClass
{
private List<string> m_Values;
...
public IEnumerable<string> Values {
get {
// Instead of enumerating and computing items
// we return the collection of computed items
return m_Values;
}
private set {
// Now value will be enumerated once and items
// will be stored in the m_Values collection
m_Values = value.ToList();
}
}
...
}
public class MyClass
{
private List<string> m_ValuesList;
private IEnumerable<string> m_Values;
...
public IEnumerable<string> Values {
get {
// Materialize if cache doesn't exist
if (m_ValuesList == null)
m_ValuesList = m_Values.ToList();
// Instead of enumerating and computing items
// we return the collection of computed items
return m_ValuesList;
}
private set {
m_Values = value;
// drop cache (materialized collection)
m_ValuesList = null;
}
}
...
}