Most people, when writing a refence type (class) which implements IComparable<T>, use the convention that null is LESS than any actual object. But if you try to use the opposite convention, something interesting happens:
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");
}
}
}
When running the above code, you see that the null references are not sorted as expected. Apparently, when comparing A to B, if A is an object and B is null, the user-defined comparision is used, but if conversely A is null and B is an object, some BCL comparision is used instead.
Is this a bug?
No, this is not a bug. Your CompareTo
method which implements IComparable<Child>
is defined on your Child
class. In other words, if you have to invoke a method on one of your types in order to make a comparison.
If one of the Child
items being compared is null, how can you invoke CompareTo
on it?
Note that from the definition of IComparable:
"By definition, any object compares greater than (or follows) a null reference (Nothing in Visual Basic), and two null references compare equal to each other."
Which explains the results you observe.
The solution is to delegate to some other class to perform the comparison. See the IComparer interface.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With