Search code examples
c#monounity-game-enginecomparetoicomparable

Generic IComparable implementation issue in Mono


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)

Solution

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