How to make an Abstract Base class IComparable that doesn't compare two separate inherited classes?

.net, c#, generics, inheritance

Solution

You can use the Curiously Recurring Template Pattern to solve this problem.

abstract class Base<T> : IComparable<T> where T : Base<T> {
    public int Rank { get; set; } // Order instances of derived type T by Rank
    public int CompareTo(T other) { return Rank.CompareTo(other.Rank); }
}
class Foo : Base<Foo> {}
class Bar : Base<Bar> {}

static class Program {
   static void Main() {
       var foo1 = new Foo { Rank = 1 };
       var foo2 = new Foo { Rank = 2 };
       var bar1 = new Bar { Rank = 1 };
       var bar2 = new Bar { Rank = 2 };

       Console.WriteLine(foo1.CompareTo(foo2));
       Console.WriteLine(bar2.CompareTo(bar1));

       //error CS1503: Argument '1': cannot convert from 'Bar' to 'Foo'
       //Console.WriteLine(foo1.CompareTo(bar1));
   }
}

Problem

(C#, VS2008) In a program I'm working on, I've got lots of objects that all have an ID and implement IComparable so that List<>-s of the various objects are easily searchable by ID. Since I hate copy/pasting code, I thought I'd abstract that bit of functionality down to a base class, like so: ``` using System; namespace MyProg.Logic { abstract class IDObject : IComparable<IDObject> { private int miID; public int ID { get { return miID; } set { miID = value; } } public IDObject(int ID) { miID = ID; } #region IComparable<IDObject> Members int IComparable<IDObject>.CompareTo(IDObject other) { return miID.CompareTo(other.miID); } #endregion } } ``` The drawback I see to that is that two separate classes that each inherit it would be directly comparable using .CompareTo() and I was hoping to enforce that each class that inherits from IDObject is only Comparable to others of the exact same class. So I was hoping to figure out how to do that and came up with this ``` using System; namespace MyProg.Logic { abstract class IDObject : IComparable<T> where T : IDObject { private int miID; public int ID { get { return miID; } set { miID = value; } } public IDObject(int ID) { miID = ID; } #region IComparable<T> Members int IComparable<T>.CompareTo(T other) { return miID.CompareTo(other.miID); } #endregion } } ``` But that gives a compile error of "Constraints are not allowed on non-generic declarations" Looking at it, I'm sure there's a way to do something like that so that each class is only comparable to other instances of that same class, but I can't tease out the syntax.

Original source