OrderBy扩展方法如何命令集合

时间:2013-06-19 08:59:47

标签: c# linq

假设我们有一个Foo类,其中Int32字段为Bar,您希望按Foo值对Bar个对象的集合进行排序。一种方法是实现IComparable的{​​{1}}方法,但也可以使用像这样的语言集成查询(LINQ)来完成

CompareTo()

现在List<Foo> foos = new List<Foo>(); // assign some values here var sortedFoos = foos.OrderBy(f => f.Bar); 我们收集了sortedFoos个已整理的集合。但是如果你使用foos对象来测量System.Diagnostics.StopWatch对集合进行排序所花费的时间总是0毫秒。但是无论何时打印OrderBy()集合,它显然都是排序的。怎么可能。文档没有时间来排序集合,但在方法执行后,集合是否已排序?有人可以向我解释一下这是如何起作用的吗?还有一件事。假设在排序sortedFoos集合后,我添加了另一个元素。现在每当我打印出集合时,我添加的元素应该在最后吗?错!对foos集合进行排序,即使我在排序后将该元素添加到foos,我添加的元素也是foos集合的一部分。我不会放弃了解任何这样的工作,所以任何人都可以为我说清楚吗?!

4 个答案:

答案 0 :(得分:5)

几乎所有LINQ方法都使用延迟评估 - 它们不会立即执行任何操作,但是当您请求数据时,它们会设置查询以执行正确的操作。

OrderBy也遵循此模型 - 尽管它比WhereSelect等方法更不懒惰。当您从OrderBy的结果中请求第一个结果时,它将从源中读取所有数据,排序所有它,然后返回第一个元素。例如,将此与Select进行比较,其中要求投影中的第一个元素仅询问来自源的第一个元素。

如果您对LINQ to Objects如何在幕后工作感兴趣,您可能需要阅读我的Edulinq blog series - LINQ to Objects的完整重新实现,其中有博客文章描述了每个方法的行为和实现。< / p>

(在我自己的OrderBy实现中,我实际上只是懒惰地排序 - 我使用快速排序并排序“足够”以返回下一个元素。这可以使largeCollection.OrderBy(...).First()之类的东西很多更快。)

答案 1 :(得分:3)

LINQ相信延迟执行。这意味着只有在开始迭代或访问结果时才会计算表达式。

答案 2 :(得分:1)

OrderBy扩展使用deault IComparer作为其正在处理的类型,除非通过适当的重载传递替代。

在首次访问语句返回的IOrderedEnumerable<T>之前,需要进行排序工作。如果您将Stopwatch放在第一次访问的周围,您将看到排序需要多长时间。

这很有道理,因为你的语句可能是由多个返回IOrderedEnumerable的调用组成的。由于排序调用是链接的,它们可以流畅地扩展结果,允许最终返回的IOrderedEnumerable以最便捷的方式执行排序。这可能是通过链接所有IComparer次调用和排序一次来实现的。贪婪的实现必须多次浪费排序。

例如,考虑

class MadeUp
{
    public int A;
    public DateTime B;
    public string C;
    public Guid D;
}

var verySorted = madeUps.OrderBy(m => m.A)
                    .ThenBy(m => m.B)
                    .ThenByDescending(m => m.C)
                    .ThenBy(m => m.D);

如果贪婪地评估verySorted,那么将评估序列中的每个属性,并且序列将被重新排序4次。因为IOrderedEnumerable的linq实现会在枚举之前进行排序,所以它能够优化该过程。

IComparerABC的{​​{1}}可以组合成一个复合委托,类似于此简化表示,

D

然后使用复合委托对序列进行一次排序。

答案 3 :(得分:0)

您必须添加ToList()才能获得新的集合。如果你没有在开始迭代sortedFoos时调用OrderBy。

List<Foo> foos = new List<Foo>();
// assign some values here
var sortedFoos = foos.OrderBy(f => f.Bar).ToList();