C# 对某些为空的IComparable对象进行排序

C# 对某些为空的IComparable对象进行排序,c#,.net,null,icomparable,C#,.net,Null,Icomparable,大多数人在编写实现IComparable的引用类型(类)时,使用null小于任何实际对象的约定。但是如果你尝试使用相反的惯例,会发生一些有趣的事情: using System; using System.Collections.Generic; namespace SortingNulls { internal class Child : IComparable<Child> { public int Age; public string Name;

大多数人在编写实现IComparable的引用类型(类)时,使用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");
    }
  }
}
使用系统;
使用System.Collections.Generic;
命名空间排序空
{
内部类子级:IComparable
{
公共信息;
公共字符串名称;
公共整数比较(其他子项)
{
如果(其他==null)
return-1;//你有什么问题?
返回此.Age.CompareTo(其他.Age);
}
公共重写字符串ToString()
{
返回string.Format(“{0}({1}年)”,this.Name,this.Age);
}
}
内部静态类程序
{
私有静态void Main()
{
var listOfChilds=新列表
{
无效的
无效的
无效的
无效的
新孩子{Age=5,Name=“Joe”},
新孩子{Age=6,Name=“Sam”},
新孩子{Age=3,Name=“Jude”},
新孩子{Age=7,Name=“Mary”},
无效的
无效的
无效的
无效的
新孩子{Age=7,Name=“Pete”},
无效的
新孩子{Age=3,Name=“Bob”},
新孩子{Age=4,Name=“Tim”},
无效的
无效的
};
listOfChilds.Sort();
WriteLine(“排序列表从这里开始”);
for(int i=0;i
运行上述代码时,您会看到空引用没有按预期排序。显然,在比较A和B时,如果A是对象,B为空,则使用用户定义的比较,但如果相反,A为空,B为对象,则使用某些BCL比较


这是一个bug吗?

不,这不是bug。实现
IComparable
CompareTo
方法在
子类上定义。换句话说,如果为了进行比较而必须调用某个类型上的方法

如果要比较的
子项
中有一项为空,如何对其调用
CompareTo

请注意,根据以下定义:

根据定义,任何对象的比较大于(或遵循)一个空引用(在Visual Basic中为零),而两个空引用的比较相等

这解释了你观察到的结果


解决方案是委托给其他类来执行比较。查看界面。

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


至于询问它是否是bug,请参见。

类型
T
的默认
比较器必须考虑第一个元素(我们称之为a)为
null
的场景。假设它看起来像这样:

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

return a.CompareTo(b);
这是基于:

此方法使用默认比较器
比较器(属于T)。T类型的默认值为
确定列表元素的顺序

可以说,如果两个元素都为
null
,则最上面的步骤只能返回
0
,否则使用
b的相反项


不过,我不会真的把它叫做虫子。这只是需要注意的一点。

不,您的代码有一个“bug”,因为它没有遵循定义
i可比较的标准。CompareTo()

具体来说:根据定义,任何对象的比较大于(或遵循)
null
,两个
null
引用的比较相等。


在您的示例中,您将对象定义为比较小于(或先于)
null
,这与应该执行的操作正好相反。

回答得很好。我没有注意到文档中说对象的比较必须大于null。当A为null且B不为null时,框架可以交换A和B,调用我的方法,然后翻转返回的Int32的符号。我认为这有时会发生在旧的非通用的IComparable上。当然,对于IComparer或Comparison委托,很容易将null作为第一个参数处理。