对于使用LINQ弄湿脚的人来说,这是一个众所周知的陷阱:
public class Program
{
public static void Main()
{
IEnumerable<Record> originalCollection = GenerateRecords(new[] {"Jesse"});
var newCollection = new List<Record>(originalCollection);
Console.WriteLine(ContainTheSameSingleObject(originalCollection, newCollection));
}
private static IEnumerable<Record> GenerateRecords(string[] listOfNames)
{
return listOfNames.Select(x => new Record(Guid.NewGuid(), x));
}
private static bool ContainTheSameSingleObject(IEnumerable<Record>
originalCollection, List<Record> newCollection)
{
return originalCollection.Count() == 1 && newCollection.Count() == 1 &&
originalCollection.Single().Id == newCollection.Single().Id;
}
private class Record
{
public Guid Id { get; }
public string SomeValue { get; }
public Record(Guid id, string someValue)
{
Id = id;
SomeValue = someValue;
}
}
}
这将显示“ False”,因为对于提供的用于创建原始集合的每个名称,select函数将不断重新评估,并且Record
重新创建结果对象。要解决此问题,ToList
可以在的末尾添加一个简单的调用GenerateRecords
。
微软希望通过这种方式获得哪些好处?
为什么实现不简单地将结果缓存在内部数组中?发生的事情的一个特定部分可能是延迟执行,但是如果没有这种行为,仍然可以实现。
一旦评估了LINQ返回的集合的给定成员,不保留内部引用/副本,而是重新计算相同的结果作为默认行为,将提供什么优势?
在逻辑上需要一遍又一遍地重新计算集合的同一成员的情况下,似乎可以通过可选参数来指定,而默认行为可以这样做。此外,通过延迟执行而获得的速度优势最终会被持续重新计算相同结果所需的时间削减。最后,对于LINQ的新手来说,这是一个令人困惑的障碍,并且可能最终导致任何人的程序中的细微错误。
这有什么好处?为什么微软做出这个看似非常故意的决定?
return listOfNames.Select(x => new Record(Guid.NewGuid(), x)).ToList();
这样就可以得到“缓存的副本”。问题解决了。