为什么使用Linq-to-Objects进行订购会将项目与自己进行比较?

Jep*_*sen 8 c# linq sorting algorithm base-class-library

考虑LINQ下面简单的代码OrderByThenBy:

static void Main()
{
  var arr1 = new[] { "Alpha", "Bravo", "Charlie", };

  var coStr = Comparer<string>.Create((x, y) =>
  {
    Console.WriteLine($"Strings: {x} versus {y}");
    return string.CompareOrdinal(x, y);
  });

  arr1.OrderBy(x => x, coStr).ToList();

  Console.WriteLine("--");

  var arr2 = new[]
  {
    new { P = "Alpha", Q = 7, },
    new { P = "Bravo", Q = 9, },
    new { P = "Charlie", Q = 13, },
  };

  var coInt = Comparer<int>.Create((x, y) =>
  {
    Console.WriteLine($"Ints: {x} versus {y}");
    return x.CompareTo(y);
  });

  arr2.OrderBy(x => x.P, coStr).ThenBy(x => x.Q, coInt).ToList();
}
Run Code Online (Sandbox Code Playgroud)

这只是使用一些比较器向控制台写出他们比较的内容.

在我的框架(.NET 4.6.2)的硬件和版本上,这是输出:

Strings: Bravo versus Alpha
Strings: Bravo versus Bravo
Strings: Bravo versus Charlie
Strings: Bravo versus Bravo
--
Strings: Bravo versus Alpha
Strings: Bravo versus Bravo
Ints: 9 versus 9
Strings: Bravo versus Charlie
Strings: Bravo versus Bravo
Ints: 9 versus 9

我的问题是:他们为什么要将查询中的项目与自身进行比较?

在第一种情况下,在--分隔符之前,它们进行四次比较.其中两个比较了一个条目("Strings:Bravo vs. Bravo").为什么?

在第二种情况下,不应该需要求助于比较Q属性(整数); 因为值中没有重复(wrt.序数比较)P,所以不需要打破平局ThenBy.我们仍然看到"Ints:9对9"两次.为什么使用ThenBy具有相同参数的比较器?

注意:任何比较器必须0在比较某些东西时返回.因此,除非算法只是想检查我们是否正确实现了比较器(无论如何它都无法完全执行),发生了什么?

请注意:我的示例中的查询所产生的元素中没有重复项.

我在另一个例子中看到了同样的问题,从查询中产生了更多的条目.上面我举一个小例子.出现这种情况有一个甚至数产生的元素,也是如此.

InB*_*een -1

好吧,让我们看看这里的可能性:

  1. T是一个值类型

    为了检查它是否正在将一个项目与自身进行比较,它首先需要检查两个项目是否相同。你会怎么做?

    您可以先打电话,如果商品不一样,Equals再打电话。CompareTo你真的想这么做吗?成本与Equals比较的成本大致相同,因此您实际上会将订购成本加倍,到底是为了什么?OrderBy只是比较所有项目,期间。

  2. T是一个引用类型

    c# 不允许您仅使用通用约束进行重载,因此您需要在运行时检查是否T是引用类型,然后调用将更改上述行为的特定实现。您想在每种情况下都承担该费用吗?当然不是。

    如果比较成本很高,则在比较逻辑中实现参考优化,以避免在将项目与其自身进行比较时产生愚蠢的成本,但该选择必须是您的。我很确定string.CompareTo正是这样做的。

我希望这能让我的答案更清楚,对之前的简短回答感到抱歉,我的推理并不那么明显。