【发布时间】:2021-10-17 22:18:00
【问题描述】:
我一直认为,如果我在 LINQ to 对象的上下文中使用 Select(x=> ...),那么新集合将立即创建并保持静态。我不太确定我为什么假设这个,这是一个非常糟糕的假设,但我做到了。我经常在其他地方使用.ToList(),但在这种情况下通常不会。
此代码表明,即使是简单的“选择”也会延迟执行:
var random = new Random();
var animals = new[] { "cat", "dog", "mouse" };
var randomNumberOfAnimals = animals.Select(x => Math.Floor(random.NextDouble() * 100) + " " + x + "s");
foreach (var i in randomNumberOfAnimals)
{
testContextInstance.WriteLine("There are " + i);
}
foreach (var i in randomNumberOfAnimals)
{
testContextInstance.WriteLine("And now, there are " + i);
}
这会输出以下内容(每次迭代集合时都会调用随机函数):
There are 75 cats
There are 28 dogs
There are 62 mouses
And now, there are 78 cats
And now, there are 69 dogs
And now, there are 43 mouses
我在很多地方都有IEnumerable<T> 作为班级成员。 LINQ 查询的结果通常分配给这样的IEnumerable<T>。通常对我来说,这不会导致问题,但我最近在我的代码中发现了一些地方,它不仅会造成性能问题。
在尝试检查我犯了这个错误的地方时,我想我可以检查一个特定的IEnumerable<T> 是否属于IQueryable 类型。我想这会告诉我收藏是否“推迟”。事实证明,上面 Select 运算符创建的枚举器的类型是 System.Linq.Enumerable+WhereSelectArrayIterator``[System.String,System.String] 而不是 IQueryable。
我使用Reflector 来查看这个接口继承自什么,结果证明它根本没有从任何表明它是“LINQ”的东西继承——因此无法根据集合类型进行测试。
我很高兴现在将.ToArray() 放在任何地方,但我希望有一个机制来确保这个问题将来不会发生。 Visual Studio 似乎知道如何做到这一点,因为它给出了一条关于“扩展结果视图将评估集合”的消息。
我想出的最好的是:
bool deferred = !object.ReferenceEquals(randomNumberOfAnimals.First(),
randomNumberOfAnimals.First());
编辑:这仅适用于使用“选择”创建新对象且不是通用解决方案的情况。无论如何我都不推荐它!这是解决方案的一个小舌头。
【问题讨论】:
-
你为什么关心延迟执行?您不应该注意这一点,并将其视为您的
IEnumerable<T>的私有实现细节。 -
因为我每次都得到不同的对象,我没想到会这样。我正在修改它们的数量,然后该更改丢失了
-
这与延迟执行有什么关系?我认为您弄错了问题的根源。
-
请记住,查询表达式为您提供了一个表示查询本身的对象。对象不代表查询的RESULTS,对象代表查询。把它想象成一个 SQL 查询字符串,只会更聪明。您向查询询问其结果,然后查询执行。你再问一次,查询再次执行;不保证您第二次询问的结果相同;从那时起,世界可能发生了变化。
-
我遇到的最好的是here,虽然不是万无一失..!
标签: c# linq linq-to-entities