Why can I not use IComparable<T> on ancestor class and compare child classes?
Asked Answered
G

1

9

I'm trying to sort a list of objects using List.Sort(), but at runtime it tells me that it cannot compare elements in the array.

Failed to compare two elements in the array

Class structure:

public abstract class Parent : IComparable<Parent> {
    public string Title;
    public Parent(string title){this.Title = title;}

    public int CompareTo(Parent other){
        return this.Title.CompareTo(other.Title);
    }
}

public class Child : Parent {
    public Child(string title):base(title){}
}

List<Child> children = GetChildren();
children.Sort(); //Fails with "Failed to compare two elements in the array."

Why can I not compare subclasses of a base that implements IComparable<T>? I'm probably missing something, but I cannot see why this should not be allowed.

Edit: Should clarify that I'm targeting .NET 3.5 (SharePoint 2010)

Edit2: .NET 3.5 is the problem (see answer below).

Galvanoscope answered 15/5, 2013 at 11:35 Comment(8)
As this seems to be a runtime exception, maybe one of your Children has a Title of null? Or maybe there is a null in your list?Wamsley
#1659161Various
It works for me with .NET 4 but not on ideone, what .NET-version are you using?Feld
Works just fine after trivially populating children.Parental
This is the exact code I'm using to provoke the error in LinqPad 2 right now (.net 3.5)Galvanoscope
@Thomas: 4.0 here, so that's where the difference is.Parental
@Mathew I know I can use the more general IComparable, but I'd really like to know WHY I cannot do it like this. Child here is effectively Parent so I shouldn't really have to implement it again right?Galvanoscope
@Soner Gönül GetChildren can be assumed to simply return a list of children. I just didn't want to fill out the question with useless code creating Child instances.Galvanoscope
J
11

I assume this is a .NET version before .NET 4.0; after .NET 4.0 it is IComparable<in T>, and should work OK in many cases - but this requires the variance changes in 4.0

The list is List<Child> - so sorting it will try to use either IComparable<Child> or IComparable - but neither of those is implemented. You could implement IComparable at the Parent level, perhaps:

public abstract class Parent : IComparable<Parent>, IComparable {
    public string Title;
    public Parent(string title){this.Title = title;}

    int IComparable.CompareTo(object other) {
        return CompareTo((Parent)other);
    }
    public int CompareTo(Parent other){
        return this.Title.CompareTo(other.Title);
    }
}

which will apply the same logic via object.

Jew answered 15/5, 2013 at 11:39 Comment(5)
I know I can use IComparable, but I really only want to support comparing two classes that derive from Parent. Child is after all a Parent here so I don't understand why I cannot simply implement it like above.Galvanoscope
Yes, but why it isn't possible to use parent-class comparer? If it were separate static function, it would be possible. Shouldn't the compiler search for base-class comparers? It sould be possible, but probably isn't implemented...Syndetic
Actually, the code works for me and sorts correctly. It does not of course work if the IComparable<Parent> implementation is removed. Update: It's definitely due to .NET 4.Parental
@Galvanoscope I assume you're targeting something before .NET 4.0? from 4.0, variance rules are improved - but prior to 4.0 it is unforgivingJew
Thanks! I'm marking this as correct since it explains a workaround and why it doesn't work for me. Good to know that this works in .NET 4.0+.Galvanoscope

© 2022 - 2024 — McMap. All rights reserved.