2

This code executes successfully in .NET 4.0

    public void CompareTest()
    {
        var m1 = new Foo { Order = 1 };
        var m2 = new Foo { Order = 2 };
        var c1 = new Bar { Order = -1 };
        var c2 = new Bar { Order = 3 };
        var list1 = new List<IOrderable> { m1, m2, c1, c2 };
        var list2 = new List<Bar> { c1, c2 };
        list1.Sort();
        list2.Sort();
    }

    public interface IOrderable : IComparable<IOrderable>
    {
        int Order { get; set; }
    }

    public class Foo : IOrderable
    {
        public int Order { get; set; }

        public int CompareTo(IOrderable other)
        {
            return Order.CompareTo(other.Order);
        }
    }

    public class Bar : IOrderable
    {
        public int Order { get; set; }

        public int CompareTo(IOrderable other)
        {
            return Order.CompareTo(other.Order);
        }
    }

However in Mono 2.6 (in Unity3D) it throws an exception:

ArgumentException: does not implement right interface

It's throwing at the second sort (list2.Sort()) but not the first

I'm kind of new to all this comparison stuff so excuse me if I'm missing something obvious here.

Why is it complaining about not implementing the right interface? It is implemented. Any ideas what's going on here? why is it working in .NET but not in Mono?

Thanks.

EDIT: Full stack

ArgumentException: does not implement right interface
System.Collections.Generic.Comparer`1+DefaultComparer[ShowEmAll.BetterBehaviourEditor+Bar].Compare (ShowEmAll.Bar x, ShowEmAll.Bar y) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Collections.Generic/Comparer.cs:86)
System.Array.compare[Bar] (ShowEmAll.Bar value1, ShowEmAll.Bar value2, IComparer`1 comparer) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/Array.cs:1744)
System.Array.qsort[Bar,Bar] (.Bar[] keys, .Bar[] items, Int32 low0, Int32 high0, IComparer`1 comparer) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/Array.cs:1721)
System.Array.Sort[Bar,Bar] (.Bar[] keys, .Bar[] items, Int32 index, Int32 length, IComparer`1 comparer) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/Array.cs:1674)
Rethrow as InvalidOperationException: The comparer threw an exception.
System.Array.Sort[Bar,Bar] (.Bar[] keys, .Bar[] items, Int32 index, Int32 length, IComparer`1 comparer) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/Array.cs:1677)
System.Array.Sort[Bar] (.Bar[] array, Int32 index, Int32 length, IComparer`1 comparer) (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System/Array.cs:1623)
System.Collections.Generic.List`1[ShowEmAll.BetterBehaviourEditor+Bar].Sort () (at /Users/builduser/buildslave/mono-runtime-and-classlibs/build/mcs/class/corlib/System.Collections.Generic/List.cs:568)
ShowEmAll.BetterBehaviourEditor.CompareTest () (at Assets/Vexe/ShowEmAll/Core/Editor/CustomEditors/BetterBehaviourEditor.cs:81)
ShowEmAll.BetterBehaviourEditor.OnEnable () (at Assets/Vexe/ShowEmAll/Core/Editor/CustomEditors/BetterBehaviourEditor.cs:62)
vexe
  • 5,433
  • 12
  • 52
  • 81
  • Please show the full exception stack trace. We've no idea what's complaining when at the moment. – Jon Skeet Jul 07 '14 at 12:49
  • So which line is 81? `list1.Sort()` or `list2.Sort()`? – Jon Skeet Jul 07 '14 at 12:53
  • I added that too to my question, but not the edit. It's the second sort that's causing the issue. `list2.Sort()` – vexe Jul 07 '14 at 12:54
  • 1
    Sounds like it's probably a Mono bug - but it works for me in Mono 3.3 – Jon Skeet Jul 07 '14 at 12:57
  • Thanks to Unity for leaving us with a rusty Mono =( – vexe Jul 07 '14 at 12:58
  • I was kinda hoping to understand why is this happening. Looks like I have to go with an alternative. What do you suggest as a best sorting alternative? `LINQ`, `IComparable`, or `IComparer`? – vexe Jul 07 '14 at 12:59
  • You could try making `Bar` also implement `IComparable` (in a pretty simple way). It *might* just work... – Jon Skeet Jul 07 '14 at 12:59
  • That breaks my logic since I want to also compare `Foo` with `Bars` :/ – vexe Jul 07 '14 at 13:02
  • Well that's not going to be an issue for `List`, is it? Given that sorting a `List` already works, it wouldn't hurt anything. See if it helps. – Jon Skeet Jul 07 '14 at 13:03
  • Thanks it worked. I also tried IComparable, it also worked. But I was kinda hoping for cleaner code with generics. It would have been be nice if I didn't have to add those extra implementations. It was silly though, all I had to do is use the other CompareTo overload in `IOrderable` :/ You can move your comment to an answer if you wish. – vexe Jul 07 '14 at 13:09

1 Answers1

2

Firstly, this looks like it's probably a Mono bug in Comparer<T>.Default, which should notice if T implements IComparable<Foo> for some Foo that is either an interface T implements, or a base type of T.

For workarounds, four options:

  • Implement the non-generic IComparable interface in Bar, probably just delegating to the implementation of CompareTo(IOrder)
  • Implement IComparable<Bar> in Bar, probably just delegating to the implementation of CompareTo(IOrder)
  • Always use a List<IOrder> even if every item in it is a Bar
  • Specify a Comparer<T> to List<T>.Sort - you can build your own implementation using generics pretty easily, I suspect.
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194