1

我正在通过这里的 101 个 Linq 教程进行编码:

http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b

大多数示例都很简单,但是这个让我陷入了循环:

    [Category("Ordering Operators")]
    [Description("The first query in this sample uses method syntax to call OrderBy and ThenBy with a custom comparer to " +
                 "sort first by word length and then by a case-insensitive sort of the words in an array. " +
                 "The second two queries show another way to perform the same task.")]
    public void Linq36()
    {
        string[] words = { "aPPLE", "AbAcUs", "bRaNcH", "BlUeBeRrY", "ClOvEr", "cHeRry", "b1" };

        var sortedWords =
            words.OrderBy(a => a.Length)
                 .ThenBy(a => a, new CaseInsensitiveComparer());

        // Another way. TODO is this use of ThenBy correct? It seems to work on this sample array.
        var sortedWords2 =
            from word in words
            orderby word.Length
            select word;

        var sortedWords3 = sortedWords2.ThenBy(a => a, new CaseInsensitiveComparer());

无论我抛出哪个单词组合,长度始终是第一个排序标准......即使我不知道第二个语句(没有 orderby!)如何知道原始 orderby 子句是什么。

我要疯了吗?谁能解释一下 Linq 如何“记住”原始顺序是什么?

4

3 回答 3

5

的返回类型OrderBy不是IEnumerable<T>。是IOrderedEnumerable<T>。这是一个“记住”所有给定顺序的对象,只要您不调用另一个将变量转换回的方法,IEnumerable它就会保留该知识。

请参阅 Jon Skeets 精彩的博客系列 Eduling,其中他重新实现了 Linq-to-objects 以获取更多信息。OrderBy/上的关键条目ThenBy是:

于 2012-12-27T19:53:16.897 回答
1

这是因为 LINQ 是惰性的,第一个即所有评估仅在您枚举序列时发生。已构建的表达式树被执行。

于 2012-12-27T19:58:38.570 回答
1

从表面上看,您的问题确实没有多大意义,因为您没有考虑延迟执行的性质。在任何一种情况下,它都不会“记住”,只是在真正需要它之前不会执行。如果您在调试器中运行您的示例,您会发现这些生成相同的(无论如何在结构上)语句。考虑:

var sortedWords =
        words.OrderBy(a => a.Length)
             .ThenBy(a => a, new CaseInsensitiveComparer());

您已明确告诉 OrderBy, ThenBy。每个语句都堆叠起来,直到它们全部完成,最终查询的构造看起来像(伪):

 Select from sorted words, order by length, order by comparer

然后,一旦一切准备就绪,它就会被执行并放入sortedWords. 现在考虑:

var sortedWords2 =
        from word in words
        orderby word.Length   // You're telling it to sort here
        select word;

// Now you're telling it to ThenBy here
var sortedWords3 = sortedWords2.ThenBy(a => a, new CaseInsensitiveComparer());

然后,一旦将这些查询堆叠起来,它将被执行。但是,在您需要它们之前不会执行它。sortedWords3在你采取行动之前不会真正有任何价值,因为对它的需求被推迟了。所以在这两种情况下,你基本上是在对编译器说:

  1. 等到我完成查询
  2. 从源中选择
  3. 按长度排序
  4. 然后通过比较器
  5. 好吧,做你的事。

注意:总而言之,LINQ 不会“记住”,它只是在您完成执行指令之前不会执行。然后它将它们堆叠成一个查询,并在需要时一次性运行它们。

于 2012-12-27T20:01:35.317 回答