6

大多数人在编写实现 IComparable<T> 的引用类型(类)时,使用的约定是 null 比任何实际对象都少。但是如果你尝试使用相反的约定,就会发生一些有趣的事情:

using System;
using System.Collections.Generic;

namespace SortingNulls
{
  internal class Child : IComparable<Child>
  {
    public int Age;
    public string Name;

    public int CompareTo(Child other)
    {
      if (other == null)
        return -1; // what's your problem?

      return this.Age.CompareTo(other.Age);
    }

    public override string ToString()
    {
      return string.Format("{0} ({1} years)", this.Name, this.Age);
    }
  }

  internal static class Program
  {
    private static void Main()
    {
      var listOfChilds = new List<Child>
      {
        null,
        null,
        null,
        null,
        new Child { Age = 5, Name = "Joe" },
        new Child { Age = 6, Name = "Sam" },
        new Child { Age = 3, Name = "Jude" },
        new Child { Age = 7, Name = "Mary" },
        null,
        null,
        null,
        null,
        new Child { Age = 7, Name = "Pete" },
        null,
        new Child { Age = 3, Name = "Bob" },
        new Child { Age = 4, Name = "Tim" },
        null,
        null,
      };

      listOfChilds.Sort();

      Console.WriteLine("Sorted list begins here");
      for (int i = 0; i < listOfChilds.Count; ++i)
        Console.WriteLine("{0,2}: {1}", i, listOfChilds[i]);
      Console.WriteLine("Sorted list ends here");
    }
  }
}

运行上述代码时,您会看到空引用未按预期排序。显然,A 和 B 比较时,如果 A 为对象且 B 为空,则使用用户定义的比较,但反之,如果 A 为空且 B 为对象,则使用一些 BCL 比较。

这是一个错误吗?

4

4 回答 4

9

不,这不是错误。您CompareTo的实现方法IComparable<Child>是在您的Child类上定义的。换句话说,如果您必须调用其中一种类型的方法才能进行比较。

如果Child要比较的项目之一为空,您如何调用CompareTo它?

请注意,根据IComparable的定义:

“根据定义,任何对象比较大于(或遵循)一个空引用(Visual Basic 中的无),并且两个空引用比较彼此相等。”

这解释了您观察到的结果。

解决方案是委托给其他类来执行比较。请参阅IComparer接口。

于 2011-05-31T15:20:38.637 回答
1

如果您尝试评估this.Age.CompareTo(other.Age);if thisis会发生什么null?事实上,this永远不可能null在 C# 中。

至于询问是否是错误,请参阅此博客文章

于 2011-05-31T15:21:38.857 回答
1

Comparer<T>类型的默认值T必须考虑到第一个元素(我们称之为 A)是null. 假设它看起来像这样:

if (ReferenceEquals(a, null))
{
    return -1;
}

return a.CompareTo(b);

这是基于以下文档List<T>.Sort

此方法使用 Comparer(Of T).Default类型 T 的默认比较器来确定列表元素的顺序。

0可以说,只有两个元素都是时,最上面的步骤才能返回null,否则使用相反的b.CompareTo(a)

不过,我真的不会称它为错误。这只是需要注意的事情。

于 2011-05-31T15:24:20.250 回答
1

不,它的代码有一个“错误”,因为它没有遵循定义的标准IComparable.CompareTo()IComparable

具体来说:根据定义,任何对象比较大于(或跟随)null,并且两个null引用比较彼此相等。

在您的示例中,您正在定义您的对象以比较小于(或之前)null,这与它应该如何完成恰恰相反。

于 2011-05-31T15:24:20.273 回答