如何判断IEnumerable< T>可以延期执行吗? [英] How to tell if an IEnumerable<T> is subject to deferred execution?

查看:84
本文介绍了如何判断IEnumerable< T>可以延期执行吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直认为,如果我在LINQ的对象中使用Select(x=> ...),那么将立即创建新集合并保持静态.我不太确定为什么要这么做,这是一个非常糟糕的假设,但我做到了.我经常在其他地方使用.ToList(),但在这种情况下通常不使用.

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

我在很多地方都有IEnumerable<T>作为班级的成员.通常将LINQ查询的结果分配给这样的IEnumerable<T>.通常对我来说,这不会引起问题,但是我最近在代码中发现了一些地方,而不仅仅是性能问题.

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.

在尝试检查我犯了这个错误的地方时,我认为我可以检查特定的IEnumerable<T>是否为IQueryable类型.我以为这可以告诉我收藏是否延期"了.事实证明,上面的Select运算符创建的枚举器的类型为System.Linq.Enumerable+WhereSelectArrayIterator``[System.String,System.String]而不是IQueryable.

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.

我使用 Reflector 来查看此接口的继承物,然后它变成完全不继承任何表明它是"LINQ"的东西-因此无法根据集合类型进行测试.

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.

我现在很高兴将.ToArray()放到任何地方,但是我希望有一种机制来确保将来不会发生此问题. Visual Studio似乎知道该怎么做,因为它给出了一条信息,扩展结果视图将评估该集合."

I'm quite happy now putting .ToArray() everywhere now, but I'd like to have a mechanism to make sure this problem doesn't 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());

编辑:仅当使用选择"创建新对象且不是通用解决方案时,此方法才有效.无论如何我都不推荐!解决方案的面颊上只有一点点舌头.

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 the cheek of a solution.

推荐答案

LINQ的延迟执行使很多人陷入困境,您并不孤单.

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:

方法的参数-除非需要更具体的接口,否则请使用IEnumerable<T>.

Parameters to methods - use IEnumerable<T> unless there's a need for a more specific interface.

局部变量-通常在创建LINQ的那一刻,所以我将知道是否可以进行惰性求值.

Local variables - usually at the point where I create the LINQ, so I'll know whether lazy evaluation is possible.

类成员-永远不要使用IEnumerable<T>,请始终使用List<T>.并始终将它们设为私有.

Class members - never use IEnumerable<T>, always use List<T>. And always make them private.

属性-使用IEnumerable<T>,并转换为设置器中的存储.

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;

尽管在理论上有些情况下这种方法行不通,但我还没有碰到过这种情况,而且自Beta以来,我一直热衷于使用LINQ扩展方法.

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.

顺便说一句:我很好奇您为什么使用ToArray();而不是ToList();-对我来说,列表具有更好的API,并且(几乎)没有性能成本.

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."

更新2 :我编写了一些代码,对数组和列表之间的性能差异进行了微基准测试.在我的笔记本电脑上,在我的特定基准测试中,每次访问之间的差异约为5ns(即 nano seconds).我猜在某些情况下,每个循环节省5ns是值得的……但我从未遇到过.在运行时足够长以进行准确测量之前,我不得不将测试最多进行100次百万次迭代.

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&lt; T&gt;可以延期执行吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆