Сортировка IComparable объектов, некоторые из которых являются нулевыми
Большинство людей, когда пишут тип ссылки (класс), который реализует IComparable
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 ответа
Нет, это не ошибка. Ваш CompareTo
метод, который реализует IComparable<Child>
определяется на вашем Child
учебный класс. Другими словами, если вам нужно вызвать метод для одного из ваших типов, чтобы сделать сравнение.
Если один из Child
сравниваемые элементы являются нулевыми, как вы можете вызвать CompareTo
в теме?
Обратите внимание, что из определения IComparable:
"По определению, любой объект сравнивает больше (или следует) нулевую ссылку (Nothing в Visual Basic), а две нулевые ссылки сравниваются равными друг другу".
Что объясняет результаты, которые вы наблюдаете.
Решение состоит в том, чтобы делегировать другому классу для сравнения. Смотрите интерфейс IComparer.
Нет, это ваш код с ошибкой, так как он не соответствует стандартам, которые определяют IComparable.CompareTo()
: IComparable
Конкретно: по определению любой объект сравнивается больше чем (или следует) null
, и два null
ссылки сравниваются равными друг другу.
В вашем примере вы определяете свой объект для сравнения меньше чем (или предшествует) null
, что в точности противоположно тому, как это должно быть сделано.
По умолчанию 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)
,
Я бы не стал называть это багом. Это просто то, что нужно знать.
Что может произойти, если вы попытаетесь оценить this.Age.CompareTo(other.Age);
если this
является null
? По факту, this
никогда не может быть null
в C#.
Что касается вопроса, если это ошибка, см. Этот пост в блоге.