Definitions and Scenario Layout
I have seen people mix up both concepts as they have a certain degree of overlapping. But only a certain degree.
What about Wikipedia’s definition: “an inequality is a statement about the relative size or order of two objects, or about whether they are the same or not”. Meaning that equality is a a boolean evaluation; whereas, when comparing two instances, we are also interested in what happens when they are not equal, tagging them with a “more” or “less” respect to the other.
“Bring in the examples ‘cause you are trying to trick me”, I can hear… Fine, there they go: As the title reads I am able to say that my father is taller than yours. It might happen that they are the same height, but that is not to say that we are brothers (unless daddy’s been “shaking the otter” around).
Enough nonsense… Is comparison an important concept? It turns out that it is, otherwise we would not have been wasting neurons in college learning about those sorting algorithms. Important as it is, the concept made its way into the .Net Framework. First in the incarnation of IComparable, and later with the looks of IComparable<T>.
Docs are clear: the result of the comparison can be negative when the subject if less than the parameter; positive when the subject is more than the parameter; or zero, when they are considered equal.
Besides, we have the comparison operators: <, >, <=, >= to complete where the interface leaves.
Let’s see how our old friend MyReferenceType handles comparison:
Which reads as:
- When I am comparing using the non-generic interface IComparable, Property1 is evaluated
- When the generic version is in use, Property2 will be the one being evaluated
So the implementors of the type have the choice to implement one, both or none of the aforementioned interfaces, but as with the IEqualityComparer family of interfaces, we are not left alone in the misery if we want to redefine how we want to sort our objects. We will be able to do that by implementing either IComparer or the generic version IComparer<T>.
And it’s not over yet. We have all those interfaces that need a new type implement them. Couldn’t we save some types? Yes, you can. In some places one can make use of the Comparison<T> delegate. The delegate has the same signature as the IComparer<T>.Compare() and it’s your best friend with anonymous methods and lambdas.
So besides MyReferenceType we have coded a helper class that redefines its ordering:
- Whenever using IComparer, Property4 is going to be evaluated
- Whenever using IComparer<T>, Property5 is going to be evaluated
- If a Comparison<T> delegate is needed, Property5 will also be the one to check
Ok, it’s about time to tell when all this comparisons are used in the framework:
- ArrayList.Sort(): As one could expect, being a framework version 1 type, it uses IComparable’s Compare(object, object) method to establish the order or the elements
- ArrayList.Sort(IComparer): I hope no one gets surprised if this method uses IComparer.Compare(object, object)
- Array.Sort(): This “mixed” version 1-2 type will use IComparer when sorting non generic arrays, but will use IComparer<T> when sorting generic arrays. It is simpler to browse the code to see the difference
- Array.BinarySearch(): same behavior as Sort()
- Array.Sort(IComparer) and Array.Sort(IComparer<T>): again it is very explicit the use of the interfaces implementors and no surprises are prepared
- Array.BinarySearch(IComparer) and Array.BinarySearch(IComparer<T>): same behavior as the previous point
- Array.Sort(Comparison<T>): this one uses a Comparison<T> so we can pass the one in our helper class, yielding the same results are if we were using IComparer<T>
- List<T>.Sort(): a generic type that uses the generic IComparable<T>, no news
- List<T>.Sort(IComparer<T>) and List<T>.Sort(IComparison<T>): again, explicit usage leads to no surprises, and that is good
- ListDictionary.ctor(IComparer): takes the non generic IComparer so it uses it. Why? This one caught me by surprise as it uses the IComparer all over the place to check for equality!! My opinion is that things like this is what makes people mix equality and comparison up, mostly when Hashtable and generic dictionary use an equality comparer incarnation (better suited for the purpose).
- SortedList: uses IComparable to order it members, and one could pass IComparer to modify that ordering
- SortedList<T>: same as the previous point but with generic counterparts
- OrderBy(keyselector, IComparer<T>): extension method: takes a “key selector” and an optional IComparable<T> to override the default sorting behavior on that key.
Comparison is an important and pervasive concept and the .Net Framework offers as many possibilities as with equality (even more, as we can use Comparison<T> delegate) to type implementors.
While writing this post, some wacky ideas occurred to me. I am eager to shape those… in a follow-up post :-)