2011-06-05 56 views
5
var res = new int[1000000].Skip(999999).First(); 

如果这个查询只使用索引器而不是遍历999999条目,那将会很棒。LINQ中为什么不跳过()以优化对象?

我看了一下System.Core.dll,发现与Skip()相比,Count()扩展方法得到了优化。如果IEnumerable执行ICollection那么它只是调用Count属性。

回答

3

如果你看一下my answer到类似的问题,看起来好像它应该很容易提供非幼稚的(即抛出适当的例外)的Skip优化任何IList

public static IEnumerable<T> Skip<T>(this IList<T> source, int count) 
    { 
     using (var e = source.GetEnumerator()) 
      while (count < source.Count && e.MoveNext()) 
       yield return source[count++]; 
    } 

当然,你的例子使用了一个数组。由于数组在迭代过程中不会抛出异常,即使像我的函数那样做任何复杂的事情都是不必要的。因此,人们可以得出结论,MS没有对其进行优化,因为他们没有想到它,或者他们认为这不是一个常见的案例值得优化。

+0

我也在讨论滥用列表迭代器的MoveNext(),但它看起来更像是一个黑客。反正好主意。 – codymanix 2011-06-05 20:08:12

3

我会让乔恩斯基特回答这个问题:

如果我们的序列是一个列表,我们就可以直接跳到它的右部和产量的项目一次一个。这听起来不错,但是如果列表在我们迭代时改变(甚至被截断!)呢?使用简单迭代器的实现通常会抛出异常,因为更改会使迭代器失效。这绝对是一种行为改变。当我第一次写关于Skip的文章时,我把它作为一个“可能的”优化 - 并且实际上在Edulinq源代码中打开了它。我现在认为这是一个错误,并已完全删除它。

...

的问题与这两个“优化”的无疑是他们正在申请用于延迟执行迭代器块中基于列表的优化。在初始方法调用点或立即执行操作符(Count,ToList等)内优先处理列表是没有问题的,因为我们假定在执行过程中序列不会改变。我们不能用迭代器块做这个假设,因为代码的流程非常不同:我们的代码会根据调用者对MoveNext()的使用重复访问。

https://msmvps.com/blogs/jon_skeet/archive/2011/01/26/reimplementing-linq-to-objects-part-40-optimization.aspx

+1

您的报价是关于名单; OP的例子是关于数组的。由于数组是固定长度的,因此它们在迭代期间不能更改,并且它们的枚举器不会引发异常。 – Gabe 2011-06-05 19:13:13

+0

@Gabe - 实际上,那篇文章的标题是“我们不能在LINQ to Objects中优化什么?”所以我想我会不同意你,因为这篇文章是关于在Linq中使用Skip()到对象 – IAmTimCorey 2011-06-05 19:14:50

+0

我一直认为,LINQ IEnumerables应该被认为是只读的。我还是不明白。 Skip的优化版本仍然会返回一个Enumerator(刚刚从索引n开始),因此如果列表更改,则表现正确。 – codymanix 2011-06-05 19:17:45

相关问题