Tuesday, October 11, 2005 - Posts

C# 3.0 and the Spaceship Operator

Abhinaba has an interesting blog entry about my all time favorite operator; Ruby’s spaceship operator <=>. This operator is a comparison operator akin to .NET IComparable interface. A part from looking cool in the editor, the operator it self isn’t the reason for my interest in it. It is the power of combining the spaceship operator with Ruby’s Comparable which’s shows the true power of mixins. If a Ruby class implements the spaceship operator, you get the <, <=, ==, >= and > operators, as well as a between? method for free just by importing the Comparable mixin.
Abhinaba points out how much simpler operator overloading would be if C# supported the spaceship operator. I agree with him on this, but with C# 3.0 we'recloser to encounters of the third kind than you might think. Regular readers of my blog might recall my post on Ruby mixins and C# 3.0 extension methods from last month. In that post I point out that extension methods resemble mixins, and since the spaceship operators true power comes from Ruby’s Comparable mixin this is a great opportunity to showcase the power of C# 3.0 extension methods.
We’ll use the IComparable<> and IComparable interfaces as a replacement for the spaceship, and since you cannot overload operators in an extension method, we’ll have to resort to named methods for the comparisons. A part from that we can get the whole shebang, including the Rubyesque between operator.

I’ve used the same Employee class as Abhinaba for my example, with the IComparable interfaces implemented.
class Employee : IComparable<Employee>, IComparable
{
    public Employee(string name, int jobGrade)
    {
        Name = name;
        JobGrade = jobGrade;
    }
    public string Name;
    public int JobGrade;

    public int CompareTo(Employee obj)
    {
        if (this.JobGrade
        else if(this.JobGrade==obj.JobGrade) return 0;
        else if (this.JobGrade>obj.JobGrade) return 1;
        return 0;
    }
    public int CompareTo(object obj)
    {
        Employee objAsEmployee=obj as Employee;
        if (objAsEmployee==null)
        {
            throw new ArgumentException("The parameter must be an Employee","obj");
        }
        return CompareTo(objAsEmployee);
    }
}

The client code is a no brainer. It just calls the extension methods on instances of the Employee class. The extension methods are mixed into the Employee implementation because I’ve added a using ComparableExtension directive to the application.
Employee employee1=new Employee("Arthur Dent",42);
Employee employee2=new Employee("Ford Prefect",10);
Employee employee3=new Employee("Trillian",15);
// Prints False
Console.WriteLine(employee1.LessThan(employee2));
// Prints True
Console.WriteLine(employee2.LessThanOrEqual(employee1));
// Prints True
Console.WriteLine(employee3.Between(employee1,employee3));

The interesting part is the implementation of the extension methods.
public static class ComparableExension
{
    public static bool GreaterThan(this object obj, object other)
    {
        return ((IComparable)obj).CompareTo(other)>0;
    }
    public static bool GreaterThanOrEqual(this object obj, object other)
    {
        return ((IComparable)obj).CompareTo(other)>=0;
    }
    public static bool EqualTo(this object obj, object other)
    {
        return ((IComparable)obj).CompareTo(other)==0;
    }
    public static bool LessThan(this object obj, object other)
    {
        return ((IComparable)obj).CompareTo(other)<0;
    }
    public static bool LessThanOrEqual(this object obj, object other)
    {
        return ((IComparable)obj).CompareTo(other)<=0;
    }
    public static bool Between(this object obj, object min, object max)
    {
        return (GreaterThanOrEqual(obj,min) && LessThanOrEqual(obj,max)) || (LessThanOrEqual(obj,min) && GreaterThanOrEqual(obj,max));
    }
}
As you can see from the code snippet above, this class is also fairly straight forward. The various comparison methods just use the IComparable interface on the objects to compare to determine whether they’re less than, equal to or greater than each other.