List <t>。排序错误排序</t>

时间:2010-03-09 17:10:06

标签: c# list sorting

我有一个对象列表,其中一些可以为null。我希望它按一些属性排序,列表末尾有null s。但是,无论比较器返回什么,List<T>.Sort()方法似乎都会将null放在开头。这是我用来测试的一个小程序:

class Program
 {
  class Foo {
   public int Bar;
   public Foo(int bar)
   {
    Bar = bar;
   }
  }
  static void Main(string[] args)
  {
   List<Foo> list = new List<Foo>{null, new Foo(1), new Foo(3), null, new Foo(100)};

   foreach (var foo in list)
   {
     Console.WriteLine("Foo: {0}", foo==null?"NULL":foo.Bar.ToString());
   }

   Console.WriteLine("Sorting:");
   list.Sort(new Comparer());
   foreach (var foo in list)
   {
    Console.WriteLine("Foo: {0}", foo == null ? "NULL" : foo.Bar.ToString());
   }
   Console.ReadKey();
  }
  class Comparer:IComparer<Foo>
  {
   #region Implementation of IComparer<in Foo>

   public int Compare(Foo x, Foo y)
   {
    int xbar = x == null ? int.MinValue : x.Bar;
    int ybar = y == null ? int.MinValue : y.Bar;
    return ybar - xbar;
   }

   #endregion
  }
 }

自己尝试一下:排序列表打印为

Foo: NULL
Foo: NULL
Foo: 100
Foo: 3
Foo: 1

null s首先,即使它们被比作int.Minvalue。方法有问题还是什么?

5 个答案:

答案 0 :(得分:13)

您的实施不正确,因为您未能首先编写规范,然后编写符合规范的代码。

所以,重新开始。写一个规范:

  • FooCompare对Foo,x和y进行两次引用。两者都可以为空。
  • IComparer.Compare(x,y)记录为如果x和y相等则返回零,如果y大于x则返回负数,如果y小于x则返回正数。我们将遵循这一惯例。
  • 如果x和y都为空,则它们相等。
  • 如果一个为null而另一个为null,那么null值为null。
  • 如果两者都不为null,则比较基于Bar属性的值。

现在您可以编写保证符合规范的代码:

// FooCompare takes two references to Foo, x and y. Either may be null.
// +1 means x is larger, -1 means y is larger, 0 means they are the same.

int FooCompare(Foo x, Foo y)
{
    // if x and y are both null, they are equal.
    if (x == null && y == null) return 0;
    // if one is null and the other one is not then the non-null one is larger.
    if (x != null && y == null) return 1; 
    if (y != null && x == null) return -1; 
    // if neither are null then the comparison is 
    // based on the value of the Bar property.
    var xbar = x.Bar; // only calculate x.Bar once
    var ybar = y.Bar; // only calculate y.Bar once
    if (xbar > ybar) return 1;
    if (ybar > xbar) return -1;
    return 0;
}

// The original poster evidently wishes the list to be sorted from
// largest to smallest, where null is the smallest.
// Reverse the polarity of the neutron flow:
int Compare(Foo x, Foo y)
{
    return FooCompare(y, x);
}

不要乱用巧妙的整数运算技巧;正如你所见,聪明的等于越野车。 首先是正确性。

答案 1 :(得分:12)

假设xnully为1。

int xbar = x == null ? int.MinValue : x.Bar;
int ybar = y == null ? int.MinValue : y.Bar;
return ybar - xbar;

现在我们有了1 - int.MinValue。这将是溢出,因此最终结果将是否定,因此null将被视为小于1.

因此,计算存在根本缺陷。

public int Compare(Foo x, Foo y)
{
    int xbar = x == null ? int.MinValue : x.Bar;
    int ybar = y == null ? int.MinValue : y.Bar;
    return ybar.CompareTo(xbar);
}

(感谢评论者指出我方法中的缺陷。)

答案 2 :(得分:4)

替换此行:

return ybar - xbar;

这一行:

return ybar.CompareTo(xbar);

因为你的算术溢出了。

答案 3 :(得分:2)

IComparer.Compare的实施不必要地复杂化。请记住,关于返回值的所有重要因素都是符号 - 幅度并不重要。要让null比实际Foo更少,只需在进行替换后遵循Int32 CompareTo的实施:

return       (x == null ? int.MinValue : x.Bar)
  .CompareTo((y == null ? int.MinValue : y.Bar))

答案 4 :(得分:0)

int.MinValue是-2147483648

现在,假设您通过减法比较3-2147483648,特别是3 - (-2147483648

此外,您应该考虑使用空合并运算符??

相关问题