2

假设我们有一个Foo具有Int32字段的类,Bar并且您想Foo按值对对象的集合进行排序Bar。一种方法是实现IComparable'sCompareTo()方法,但也可以像这样使用语言集成查询(LINQ)来完成

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

现在sortedFoos我们有foos已排序的集合。但是如果你使用System.Diagnostics.StopWatchobject 来衡量对集合进行排序所花费的时间OrderBy()总是 0 毫秒。但是每当你打印sortedFoos集合时,它显然是排序的。这怎么可能。文学没有时间对集合进行排序,但是在方法执行之后集合被排序?有人可以向我解释这是如何工作的吗?还有一件事。假设在对foos集合进行排序后,我向它添加了另一个元素。现在每当我打印出集合时,我添加的元素最终应该是对的吗?错误的 !集合将foos被排序,我添加的元素是集合的一部分,foos即使我将该元素添加到foos排序后。我不明白这些是如何工作的,所以任何人都可以为我说清楚吗?!

4

4 回答 4

5

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

OrderByWhere也遵循这个模型——尽管它没有像and之类的方法那么懒惰Select。当您从 的结果中请求第一个结果时OrderBy,它将从源中读取所有数据,对所有数据进行排序,然后返回第一个元素。例如,将Select其与请求投影中的第一个元素仅要求源中的第一个元素进行比较。

如果您对 LINQ to Objects 如何在幕后工作感兴趣,您可能想阅读我的Edulinq 博客系列- LINQ to Objects 的完整重新实现,其中包含描述每种方法的行为和实现的博客文章。

(在我自己的 实现中OrderBy,我实际上只是懒惰地排序——我使用快速排序并“刚好”排序以返回下一个元素。这可以使事情变得largeCollection.OrderBy(...).First()更快。)

于 2013-06-19T09:03:36.940 回答
3

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

于 2013-06-19T09:02:49.430 回答
1

OrderBy扩展使用它正在处理的类型的默认IComparer值,除非通过适当的重载传递替代方案。

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

这很有意义,因为您的语句可以由多个返回IOrderedEnumerables 的调用组成。由于排序调用被链接起来,它们流畅地扩展了结果,允许最终返回的结果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 次。因为 linq 的实现将IOrderedEnumerable排序推迟到枚举,所以它能够优化过程。

IComparers 代表, A,B和可以组合成一个复合委托CD类似于这个简化的表示,

int result
result = comparerA(A, B);
if (result == 0)
{
    result = comparerB(A, B);
    if (result == 0)
    {
        result = comparerC(A, B);
        if (result == 0)
        {
            result = comparerD(A, B);
        }
    }
}

return result;

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

于 2013-06-19T09:02:47.233 回答
0

您必须添加 ToList() 以获取新集合。如果你不这样做,OrderBy 将在你开始迭代 sortedFoos 时被调用。

List<Foo> foos = new List<Foo>();
// assign some values here
var sortedFoos = foos.OrderBy(f => f.Bar).ToList();
于 2013-06-19T09:06:54.250 回答