posted on Tuesday, October 11, 2005 8:09 AM
by
anoras
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.