问题描述
我总是假设,如果我用选择(X => ...)
在LINQ到对象的上下文,那么新的集合将立即创建并保持不变。我不明白为什么我以为这和它的一个非常坏的假设,但我做到了。我经常使用 .ToList()
其他地方,但往往不是在这种情况下。
此代码演示,即使是简单的选择受延迟执行:
VAR随机=新的随机();
变种动物=新[] {猫,狗,老鼠};
变种randomNumberOfAnimals = animals.Select(X => Math.Floor(random.NextDouble()* 100)++ X +的s);
的foreach(VAR我在randomNumberOfAnimals)
{
testContextInstance.WriteLine(有+ I);
}
的foreach(VAR我在randomNumberOfAnimals)
{
testContextInstance.WriteLine(现在,有+ I);
}
这输出以下(随机函数被调用每一个集合的迭代时间通过):
有75猫
有28个狗
有62鼠标
和现在,还有78只猫
而现在,有69个狗
而现在,有43个鼠标
我有很多地方我有一个的IEnumerable< T>
作为一个类的成员。通常,LINQ查询的结果被分配到这样的的IEnumerable< T>
。通常,对我来说这不会造成问题,但我最近发现我的代码它带来的不仅仅是性能问题更加的几个地方。
在试图检查在那里我做了这样的错误,我想我可以检查的地方如果一个特定的的IEnumerable< T>
是类型的IQueryable
。这一点,我想会告诉我,如果集合是'推迟'与否。事实证明,通过上述选择运营商创建的枚举类型是 System.Linq.Enumerable + WhereSelectArrayIterator`` [System.String,System.String]
,而不是<$的C $ C>的IQueryable 。
我用的来看看来自继承了这个接口,和原来不从任何表明它是LINQ在所有的继承 - 所以没有办法根据集合类型测试。 p>
我很愉快,现在把 .ToArray()
现在到处都是,但我想有一个机制确保在今后的犯规发生这个问题。 Visual Studio中似乎知道如何,因为它提供了有关消息做
我想出的最好的'扩大结果视图将评估集合。:
布尔递延= object.ReferenceEquals(randomNumberOfAnimals.First(),
randomNumberOfAnimals.First())!;
编辑:仅在一个新的对象与创建该作品的选择它不是一个通用的解决方案。我不是在任何情况下推荐它虽然!这是一个解决方案的脸颊小舌头。
LINQ的延迟执行已被困了很多人,你。不是一个人
我已经采取了避免这个问题的方法如下:
的参数方法的 - 使用的IEnumerable< T>
除非有需要更具体的接口
的局部变量的 - 通常在那里我创建了LINQ的地步,所以我就知道偷懒评估是否可能
的类成员的 - 从来没有使用的IEnumerable< T>
,总是用列表< T>
。 。总是让他们的私人
的属性的 - 使用的IEnumerable< T>
和。转换为存储在二传手
公开的IEnumerable<&人GT;人们
{
{返回万人; }
集合{=人value.ToList(); }
}
私有列表<人与GT;人;
虽然有理论的情况下,这种做法是行不通的,我没有跑成一个尚未,我一直在用热情地下旬以来的贝塔LINQ扩展方法
BTW:为什么你用我很好奇 ToArray的();
而不是了ToList();
- 对我来说,名单有一个更加美好的API,并有(几乎)没有任何性能开销
更新:一对夫妇评论者都正确地指出,阵列具有理论上的性能优势,所以我修改上面我的发言...有(几乎)没有任何性能开销
更新2 :我写了一些代码,做差价的一些微基准测试在阵列之间的性能和列表。在我的笔记本电脑,在我的具体性能指标评测,所不同的是周围的5ns每次访问(这是的纳米的秒)。我想有这样的情况:每个回路节电5ns的将是值得的......但我从来没有碰到过的。我不得不加息我的测试高达100的亿的运行之前迭代成为足够长的精确测量。
I always assumed that if I was using Select(x=> ...)
in the context of LINQ to objects, then the new collection would be immediately created and remain static. I'm not quite sure WHY I assumed this, and its a very bad assumption but I did. I often use .ToList()
elsewhere, but often not in this case.
This code demonstrates that even a simple 'Select' is subject to deferred execution :
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);
}
This outputs the following (the random function is called every time the collection is iterated through):
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
I have many places where I have an IEnumerable<T>
as a member of a class. Often the results of a LINQ query are assigned to such an IEnumerable<T>
. Normally for me this does not cause issues, but I have recently found a few places in my code where it poses more than just a performance issue.
In trying to check for places where I had made this mistake I thought I could check to see if a particular IEnumerable<T>
was of type IQueryable
. This i thought would tell me if the collection was 'deferred' or not. It turns out that the enumerator created by the Select operator above is of type System.Linq.Enumerable+WhereSelectArrayIterator``[System.String,System.String]
and not IQueryable
.
I used Reflector to see what this interface inherited from, and it turns out not to inherit from anything that indicates it is 'LINQ' at all - so there is no way to test based upon the collection type.
I'm quite happily now putting .ToArray()
everywhere now, but I'd like to have a mechanism to make sure this problem doesnt happen in future. Visual Studio seems to know how to do it because it gives a message about 'expanding the results view will evaluate the collection.'
The best I have come up with is :
bool deferred = !object.ReferenceEquals(randomNumberOfAnimals.First(),
randomNumberOfAnimals.First());
Edit: This only works if a new object is created with 'Select' and it not a generic solution. I'm not recommended it in any case though! It was a little tongue in cheek of a solution.
Deferred execution of LINQ has trapped a lot of people, you're not alone.
The approach I've taken to avoiding this problem is as follows:
Parameters to methods - use IEnumerable<T>
unless there's a need for a more specific interface.
Local variables - usually at the point where I create the LINQ, so I'll know whether lazy evaluation is possible.
Class members - never use IEnumerable<T>
, always use List<T>
. And always make them private.
Properties - use IEnumerable<T>
, and convert for storage in the setter.
public IEnumerable<Person> People
{
get { return people; }
set { people = value.ToList(); }
}
private List<People> people;
While there are theoretical cases where this approach wouldn't work, I've not run into one yet, and I've been enthusiasticly using the LINQ extension methods since late Beta.
BTW: I'm curious why you use ToArray();
instead of ToList();
- to me, lists have a much nicer API, and there's (almost) no performance cost.
Update: A couple of commenters have rightly pointed out that arrays have a theoretical performance advantage, so I've amended my statement above to "... there's (almost) no performance cost."
Update 2: I wrote some code to do some micro-benchmarking of the difference in performance between Arrays and Lists. On my laptop, and in my specific benchmark, the difference is around 5ns (that's nanoseconds) per access. I guess there are cases where saving 5ns per loop would be worthwhile ... but I've never come across one. I had to hike my test up to 100 million iterations before the runtime became long enough to accurately measure.
这篇关于如何判断一个IEnumerable< T>受延迟执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!