Sort IComparable objects, some of which are null

Most people, when writing a refence type (class) that implements IComparable <T>, use a convention whose null value is less than any actual object. But if you try to use the opposite convention, something interesting will happen:

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 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 you run the above code, you see that null links are not sorted as expected. Apparently, when comparing A with B, if A is an object and B is null, a user comparison is used, but if A is null and B is an object, some BCL comparison is used instead.

This is mistake?

+6
source share
4 answers

No, this is not a mistake. Your CompareTo method, which implements IComparable<Child> , is defined in your Child class. In other words, if you need to call a method on one of your types to make a comparison.

If one of the Child items being compared is null, how can you call CompareTo on it?

Note that from the definition of IComparable :

"By definition, any object compares more (or follows) a null reference (Nothing in Visual Basic), and two null references are compared with each other."

This explains the results that you are observing.

The solution is to delegate to another class to perform the comparison. See IComparer interface.

+8
source

What is expected if you try to evaluate this.Age.CompareTo(other.Age); if this is null ? In fact, this never be null in C #.

Regarding the bug issue, see this blog post .

+1
source

By default, Comparer<T> for type T should consider a scenario in which the first element (let it be called A) is null . Let's say it looks something like this:

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

This is based on the List<T>.Sort :

This method uses the default match Comparer(Of T).Default for type T to determine the order of the list items.

Perhaps the top step could only return 0 if both elements are null , and otherwise use the opposite value of b.CompareTo(a) .

I would not call it a mistake. This is just what you need to know.

+1
source

No, this is your code that has an โ€œerrorโ€ because it does not comply with the standards defining IComparable.CompareTo() : IComparable

In particular: By definition, any object is compared more (or follows) null , and two null references are compared with each other.

In your example, you define your object to be compared with less (or preceded by) null , which is exactly the opposite of how it should be done.

+1
source

Source: https://habr.com/ru/post/889463/


All Articles