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).

Jehof
  • 34,674
  • 10
  • 123
  • 155
Thomas
  • 1,512
  • 3
  • 12
  • 37
  • 2
    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? – nvoigt May 15 '13 at 11:40
  • http://stackoverflow.com/questions/1658661/failed-to-compare-two-elements-in-the-array – Mat J May 15 '13 at 11:40
  • 3
    It works for me with .NET 4 but [**not on ideone**](http://ideone.com/rZ0rVL), what .NET-version are you using? – Tim Schmelter May 15 '13 at 11:41
  • Works just fine after trivially populating `children`. – Jon May 15 '13 at 11:41
  • This is the exact code I'm using to provoke the error in LinqPad 2 right now (.net 3.5) – Thomas May 15 '13 at 11:42
  • @Thomas: 4.0 here, so that's where the difference is. – Jon May 15 '13 at 11:43
  • @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? – Thomas May 15 '13 at 11:43
  • @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. – Thomas May 15 '13 at 11:57

1 Answers1

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 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. – Thomas May 15 '13 at 11:41
  • 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... – Sasha May 15 '13 at 11:41
  • Actually, the code works for me and sorts correctly. It does not of course work if the `IComparable` implementation is removed. *Update*: It's definitely due to .NET 4. – Jon May 15 '13 at 11:42
  • 3
    @Thomas I assume you're targeting something before .NET 4.0? from 4.0, variance rules are *improved* - but prior to 4.0 it is unforgiving – Marc Gravell May 15 '13 at 11:43
  • 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+. – Thomas May 15 '13 at 11:45