具有计算值的 Linq Select 投影

Linq Select projections with calculated values

我在将 Select 与匿名函数或运行时计算的其他值一起使用时注意到的是,每次访问输出 IEnumerable 对象时,投影都会重新计算该值。示例:

public class A
{
    public string Name { get; set; }
    public string Addr { get; set; }
}
public class B
{
    public A Whatever {get;set;}
    public int Id {get;set;}
}

Random rand = new Random();
IEnumerable<B> listOfBs = someListOfA.Select( x => new B()
{
    Id = rand.Next(),
    Whatever = x
});

我注意到,每次我解析列表 listOfB 并使用 Id 属性 时,都会用另一个 rand.Next().

重新评估该 Id
foreach( B b in listOfBs )
{
  doSomething( b.Id );
}

我在有关 C# 投影的文档中没有看到会导致此问题的原因。它几乎就像 Select 生成一个匿名函数,每次访问时都会重新计算。那么,两个问题:

  1. 我看到的是什么行为。
  2. 如何避免这种情况,但仍然能够将一种类型的列表转换为另一种类型的列表。

如果我的错误示​​例代码表达了我的观点,请告诉我。

这是预料之中的,因为大多数 LINQ 方法都是惰性的——这意味着它们在需要结果之前不会枚举源。在您的特定场景中 listOfBs 实际上并不是 B 对象的物化集合。相反,它是关于 如何someListOfA 转换为 B 对象集合的定义。 Select returns 实现 IEnumerable<T> 并存储对源集合和投影委托的引用的对象。投影是在需要结果时完成的,例如当您遍历 foreach 中的集合时。如果您多次迭代,投影将被执行多次。这正是您所看到的。

调用ToListToArray立即实现结果:

IEnumerable<B> listOfBs = someListOfA.Select( x => new B()
{
    Id = rand.Next(),
    Whatever = x
}).ToList();