A Mixin for IComparable<T>


Following on from my other posts on C# Mixins, here’s a short one to demonstrate the benefits of Mixins using IComparable<T>.

I don’t know about you, but I can never remember how the CompareTo method of IComparable<T> works. If I remember correctly, it gives back -1 if the value of the compared object is less than the value of the called object, and +1 if the compared object is greater than the value of the called object.

No, wait! That’s the wrong way round! See what I mean?

The CompareTo method is defined like this:

int CompareTo(T other)

According to the MSDN Library, the value it returns is:

Value Meaning
Less than zero This object is less than the other parameter.
Zero This object is equal to other.
Greater than zero This object is greater than other.

Now I don’t know about you, but I always have trouble with that. Mix-ins to the rescue!

What I really need is to define my own methods on the IComparable<T> interface. Something like LessThan, MoreThan and ValueEquals. That would be much more valuable. I could define those methods in a superclass, and have my new classes all inherit from that superclass. But that would bind me to a certain structure, reduce the coherency of my classes, and make me feel bad. But if I implement IComparable<T> with its sole method, I can use a Mixin to take advantage of that method and add the new functionality I need, without affecting the structure of my code.

Here’s an example using the Temperature class shamelessly lifted from the MSDN Library.

The class Temperature is defined as:

public class Temperature : IComparable<Temperature>
{
  // Implement the CompareTo method. For the parameter type, Use
  // the type specified for the type parameter of the generic
  // IComparable interface.
  //
  public int CompareTo(Temperature other)
  {
    // The temperature comparison depends on the comparison of the
    // the underlying Double values. Because the CompareTo method is
    // strongly typed, it is not necessary to test for the correct
    // object type.
    return m_value.CompareTo(other.m_value);
  }

  // The underlying temperature value.
  protected double m_value = 0.0;

  public Temperature(double degreesKelvin)
  {
    this.Kelvin = degreesKelvin;
  }
}

Note that Temperature implements IComparable<Temperature>.

So now if you define a static extension class for IComparable<T>, you should also be able to use it with IComparable<Temperature>.

Here are the extension methods I’m going to add – LessThan, MoreThan, and ValueEquals:

public static class IComparableExtensions
{
  public static bool LessThan<T>(this IComparable<T> comparable, T other)
  {
    return comparable.CompareTo(other) < 0;
  }

  public static bool MoreThan<T>(this IComparable<T> comparable, T other)
  {
    return comparable.CompareTo(other) > 0;
  }

  public static bool ValueEquals<T>(this IComparable<T> comparable, T other)
  {
    return comparable.CompareTo(other) == 0;
  }
}

My program now references the IComparableExtensions class by importing its namespace. I then write:

var t1 = new Temperature(273);
var t2 = new Temperature(100);

All I had to do was implement IComparable<T> with its one CompareTo method (which was trivial), and I automatically get the extension methods LessThan, MoreThan and ValueEquals mixed-in to my Temperature class.

if (t1.LessThan(t2))
  Console.WriteLine("t1 is less than t2");
else if (t1.MoreThan(t2))
  Console.WriteLine("t1 is more than t2");

And this doesn’t just work with IComparable<Temperature>, it works with anything which implements IComparable<T>. Classes implementing IComparable<int> would also get access to the new methods, for example.

Here’s the output of the code:

t1 is more than t2

… as you would expect.

And voila! That’s why mix-ins can be so useful.

I’ve intentionally done this example using a well-known interface, to make it easier to understand. But imagine what you could do by inheriting from a particular class of your own, defining your own interface, and then adding in more functionality using a mixin. You’ve nearly got multiple inheritance.

More to come on this…

You can follow any responses to this entry through the RSS 2.0 feed.