对IComparable对象进行排序,其中一些对象为null

时间:2011-05-31 15:14:56

标签: c# .net null icomparable

大多数人在编写实现IComparable< T>的refence类型(类)时,使用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是null,则使用用户定义的比较,但如果相反A为空且B是对象,则使用某些BCL比较。

这是一个错误吗?

4 个答案:

答案 0 :(得分:8)

不,这不是错误。实施CompareTo的{​​{1}}方法已在IComparable<Child>课程中定义。换句话说,如果你必须在你的一个类型上调用一个方法来进行比较。

如果要比较的Child项之一为空,您如何在其上调用Child

请注意IComparable的定义:

“根据定义,任何对象都比空(或跟随)空引用(在Visual Basic中为Nothing),并且两个空引用相互比较相等。”

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

解决方案是委托其他一些类来执行比较。请参阅IComparer界面。

答案 1 :(得分:1)

如果this.Age.CompareTo(other.Age); thisnull,您试图评估this会发生什么?实际上,null在C#中永远不会是{{1}}。

至于询问是否是错误,请参阅this blog post

答案 2 :(得分:1)

类型Comparer<T>的默认T必须考虑第一个元素(我们称之为A)为null的情况。让我们说它看起来像这样:

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

return a.CompareTo(b);

这基于the documentation of List<T>.Sort

  

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

可以说,如果两个元素都是0,那么最上一步只能返回null,否则请使用b.CompareTo(a)的反面。

我不会把它称为 bug 。这只是需要注意的事情。

答案 3 :(得分:1)

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

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

在您的示例中,您定义的对象要比null小于(或先于){},这恰好与应该如何进行比较。