Automatic comparison operator overloading in C#

Abhinaba has blogged on the painful C# operator overloading experience.

The basic problem that is if you want to overload one, you soon end up many of the tests including == != < <= > >= Equals, Compare and probably CompareTo via IComparable when it turns out one function can provide everything needed in general use.

GitHub has the latest version of AutoOperators

Ruby already has this with the <=> operator, sometimes known as a SpaceShipOperator and this got me thinking resulting in the following little class:

using System;

/// AutoOperators provides a base class where all the standard operators are overridden and
/// wired up via the subclass's implementation of IComparable.CompareTo.
public abstract class AutoOperators : IComparable {
  public static bool operator < (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) < 0;
  }
  public static bool operator > (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) > 0;
  }
  public static bool operator == (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) == 0;
  }
  public static bool operator != (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) != 0;
  }
  public static bool operator <= (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) <= 0;
  }
  public static bool operator >= (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) >= 0;
  }

  public static int Compare(AutoOperators obj1, AutoOperators obj2) {
    if (Object.ReferenceEquals(obj1, obj2)) return 0;
    if ((object)obj1 == null) return -1;
    if ((object)obj2 == null) return 1;
    return obj1.CompareTo(obj2);
  }

  public abstract int CompareTo(object obj);
  public abstract override int GetHashCode();

  public override bool Equals(object obj) {
    if (!(obj is AutoOperators)) return false;
    return this == (AutoOperators) obj;
  }
}

And then to use it simply inherit from it and implement GetHashCode and CompareTo, e.g.

using System;

public class SampleClass : AutoOperators {
  private int testValue = 0;

  public SampleClass(int initialTestValue) {
    testValue = initialTestValue;
  }

  public int TestValue {
    get { return testValue; }
    set { testValue = value; }
  }

  public override int CompareTo(object obj) {
    if (obj is SampleClass)
       return TestValue.CompareTo(((SampleClass) obj).TestValue);
    else
       return -1;
  }

  public override int GetHashCode() {
    return TestValue.GetHashCode();
  }
}

As with all code, determine the suitability of this solution to your own needs – that responsibility is yours. As with all code here it comes without warranty, expressed, implied or otherwise alluded to.

I’d recommend checking out the performance, CLS compliance regarding operators and the addition of any extra operators you use all the time.

[)amien

1 responses

  1. Avatar for bob r

    Very nice. Saves me quite a bit of typing.

    A (very) minor issue:

    public override bool Equals(object obj) {
        if (!(obj is AutoOperators)) return false;
        return this == (AutoOperators) obj;
    }
    

    Will cause "Code Analysis" (FxCop ??) to issue a "Warning 1 CA1800 : Microsoft.Performance : 'obj', a parameter, is cast to type 'AutoOperators' multiple times in method 'AutoOperators.Equals(object)'. Cache the result of the 'as' operator or direct cast in order to eliminate the redundant castclass instruction. ..\AutoOperators.cs 54"

    Changing it to:

    public override bool Equals(object obj) {
        AutoOperators other = obj as AutoOperators;
        return (other != null) ? (this == other) : false;
    }
    

    makes the "Code Analysis" program happy. The same issue arises in the "CompareTo" override.

    The if/else structure does work better if the comparison requires more than a simple expression while the conditional operator is very concise if a simple expression will do the job.

    Thanks, Bob R

    bob r 19 July 2010