views:

388

answers:

3

Okay so I have a scenario similar to the below code, I have a parent class that implements IComparable and a child class.

class Parent : IComparable<Parent> 
class Child : Parent

Child a = new Child();
Child b = new Child();
a.CompareTo(b);

Now the above works fine, i can compare two of the child objects to each other no problem

List<Child> l = new List<Child>();
l.Add(b);
l.Add(a);
l.Sort();

The above fails though with an InvalidOperationException. Can someone please explain why this sort isn't working when the child class does implement the IComparable interface, or at least it seems to me that it does.

Okay here is my CompareTo implementation for my actual parent class

 public int CompareTo(IDType other)
 {
       return this.Name.ToString().CompareTo(other.ToString());
 }
+5  A: 

Your type implements IComparable<Parent> rather than IComparable<Child>. As the MSDN docs for Sort say, it will throw InvalidOperationException if "the default comparer Comparer(T).Default cannot find an implementation of the IComparable(T) generic interface or the IComparable interface for type T." And indeed it can't, where T is Child. If you try making it a List<Parent> you may well find it's fine.

EDIT: Alternatively (and preferably, IMO) implement IComparable<Child>. At the moment it's not at all clear that a child can sensibly be compared with another child. Implementing IComparable<Child> - even if that implementation just defers to the base implementation - advertises the comparability.

Jon Skeet
No my comparison works fine, I'm simply using the compare function of a string member variable that is stored in the parent class.
CalvinR
Changed answer completely now, having checked something else :)
Jon Skeet
Man that's absolute garbage, it works fine if I implement the non-generic IComparable in the parent class.
CalvinR
+3  A: 

I gave this a try and ran into the same error. My guess is that because you've got a List<Child> it's looking for someone that implements IComparable<Child>, not IComparable<Parent>.

If you change the collection to List<Parent>, things seem to work. Alternatively, have Child implement IComparable<Child> (and it can just delegate to Parent's implementation).

John Price
Yeah unfortunately I'm not going to be changing my collections to work that way any time soon so I guess I'll just have to implement the non-generic IComparable or just implement the generic one in each of the classes I want to sort.
CalvinR
+1  A: 

My guess is that is has something to do with the covariance/contravariance stuff. The reason I say that is because this fails like you said:

 public class Parent : IComparable<Parent>
    {
        public int Age { get; set; }


        public int CompareTo(Parent other)
        {

            return this.Age.CompareTo(other.Age);
        }

    }

    public class Child : Parent
    {
        public string Name { get; set; }
    }

However, when I changed the parent to implement the non generic IComparable and did this, it worked:

    public class Parent : IComparable
    {
    public int Age { get; set; }


    public int CompareTo(object other)
    {
        Parent p = other as Parent;
        return this.Age.CompareTo(p.Age);
    }

}

    public class Child : Parent
    {
        public string Name { get; set; }
    }

Code I tested it with:

    List<Child> c = new List<Child>();
    c.Add(new Child { Age = 10, Name = "Alex" });
    c.Add(new Child { Age = 6, Name = "Jack" });
    c.Add(new Child { Age = 15, Name = "Bob" });

    c.Sort();
BFree
Yeah i found the same thing, I was just hoping I could still use the generic form of Icomparable.
CalvinR