Someone asked a question on StackOverflow about the difference between the int CompareTo method and an ordinary If condition. As it turns out, the poster’s `if` statement is exactly how `Int32` implements `CompareTo`.

The `IComparable` and `IComparable<T>` interfaces each have only a single method, which can be used to compare two objects for the purpose of determining how they should be ordered relative to one another.

`````` // IComparable<T> - Compares the current object with another object of the same type.
int CompareTo(T other);

// IComparable - Compares the current object with another object.
int CompareTo(object obj);
``````

The return value, an `int`, indicates the relative order:

• < 0: This object is less than the other parameter.
• = 0: This object is equal to the other object.
• 0: This object is greater than the other object.

All primitive types implement these interfaces, and the result of `CompareTo` is fairly intuitive in most cases. Following are the details of how each type implements `IComparable<T>`. (Note that `m_value` represents the value of the current integer or double, and `value` is the other integer or double being compared to the current value.)

Int32:

``````if (m_value < value)
return -1;
if (m_value > value)
return 1;
return 0;
``````

By far, the most straight-forward. The other x-bit types are implemented the same way (`Int64` (long), `Int16` (short) and byte).

Double:

``````if (m_value < value)
return -1;
if (m_value > value)
return 1;
if (m_value == value)
return 0;

if (IsNaN(m_value))
return (IsNaN(value) ? 0 : -1);
else
return 1;
``````

Note the `IsNaN` test. If you try to act on a non-number such as the result of `0/0`, then `<`, `>` and `==` do not apply, and logic drops down to `IsNaN`. Read more about IsNaN.

Preference is given to the object that is a valid number. If the double you’re comparing is a valid number, then the double you’re comparing to must be invalid and it treats the first as greater.

If the double you’re comparing is invalid, logic drops through to the last line. If the double you’re comparing to is valid, the first is treated as less. If they’re both invalid, they’re considered equal for the sake of this test (although technically two non-numbers are not equal).

The implementation of `Single` (also a floating point type) is the same.

Decimal:

``````Decimal.FCallCompare(ref this, ref value);
``````

This one calls a method that is marked with `MethodImplOptions.InternalCall`. The only reference I could find was from the knowledgeable Hans Passant on SO. The code is buried in some C++ files within the CLR. Some basic testing shows that the comparison is the same as `Int32`.

Byte:

``````return (m_value - value);
``````

The implementation here is basically the same as with `Int32` – it performs an arithmetic operation on the values. If the result is `0`, then they must be the same byte.

Char:

``````return (m_value-value);
``````

Since the default behavior of using an arithmetic operator on a `char` type is to convert it to an integer first, the implementation here is the same as the others.

Bool:

``````if (m_value == value)
return 0;
else if (m_value == false)
return -1;
return 1;
``````

`false` is considered “less than” `true`.

DateTime:

``````long valueTicks = value.InternalTicks;
long ticks = InternalTicks;
if (ticks > valueTicks)
return 1;
if (ticks < valueTicks)
return -1;
return 0;
``````

Compares using the `Ticks` of the two `DateTime` instances, similar to `Int64`. It's a little interesting that the `Ticks` values are assigned to temporary variables prior to the comparison. I did some research but couldn't find anything explaining why this is. If you know why, please leave a comment!

String:

``````if (strB == null)
return 1;
return CultureInfo.CurrentCulture.CompareInfo.Compare(this, strB, CompareOptions.None);
``````

If the string being compared to is `null`, then the first string is considered "greater than" the second. Otherwise, logic drops into `CompareInfo.Compare`, where there's the following comment:

Our paradigm is that null sorts less than any other string and that two nulls sort as equal.

The method found there is longer then what I've included below, but here's the relevant part which treats `null` as less than any value, and treats two `null` values as equal.

``````if (string1 == null)
{
if (string2 == null)
return 0;     // Equal
return -1;        // null < non-null
}
if (string2 == null)
return 1;         // non-null > null
``````

The above types implement `IComparable` as well, and it's nearly identical to `IComparable<T>` in each case. The only significant difference is that, since the parameter is an `object` and not a primitive type, an initial check returns `1` if the parameter is `null`, and if the parameter is not the same primitive type and `ArgumentException` is thrown. Here's the implementation from `Int32`:

``````if (value == null)
return 1;
if (!(value is int))
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeInt32"));
``````

You can implement `IComparable<T>` in your own class as well. The terms "less than", "equal to" and "greater than" are completely subjective, and you determine what they mean in the context of ordering two instances of your own object relative to one another.

Here's a quick sample class, in which I've implemented both interfaces using similar logic to the primitive types. One `MySampleClass` object is greater than another if it's `Id` value is greater.

``````public class MySampleClass : IComparable, IComparable<mysampleclass>
{
public int Id { get; set; }
public int CompareTo(object obj)
{
if (obj == null)
return 1;
if (!(obj is MySampleClass))
throw new ArgumentException("Argument must be an instance of MySampleClass");
var otherId = ((MySampleClass)obj).Id;
if (Id > otherId)
return 1;
if (Id < other.Id)
return -1;
return 0;
}
}
``````