Search code examples
c#sortinginterfacecomparetoicomparable

Sorting list of interface reference objects inherting ICompareable<T>


I was having trouble with list.Sort() for a list of interface references that point to different types however the question Sort a list of interface objects provided the following solution solution

interface IFoo : IComparable<IFoo> 
{ 
    int Value { get; set; } 
}

class SomeFoo : IFoo
{
    public int Value { get; set; }

    public int CompareTo(IFoo other)
    {
        // implement your custom comparison here...
    }
}

In my original code rather than IFoo inherting from IComparable my class was inherting from both IFoo and ICompareable i.e.

interface IFoo
{ 
    int Value { get; set; } 
}

class SomeFoo : IFoo, IComparable<IFoo> 
{
    public int Value { get; set; }

    public int CompareTo(IFoo other)
    {
        // implement your custom comparison here...

    }
}
class SomeBar : IFoo, IComparable<IFoo> 
{
    public int Value { get; set; }

    public int CompareTo(IFoo other)
    {
        // implement your custom comparison here...
    }
}

But I was getting the error Failed to compare two elements in the array. when trying to sort a list of IFoo references.

List<IFoo> iFoos = new List<IFoo> 
{
    new SomeFoo{Value = 1},
    new SomeFoo{Value = 15},
    new SomeFoo{Value = 390},
    new SomeBar{Value = 2},
    new SomeBar{Value = 78},
    new SomeBar{Value = 134}
}

iFoos.Sort();

Can anyone explain why my original code did not work?


Solution

  • Your list is a list of IFoos. So from the perspective of the list (and its sort operation), it only sees that interface and does not know anything about the concrete types.

    So when it tries to order two IFoos, it cannot do that because IFoo does not implement IComparable.

    The problem is that just because both your types implement IComparable<Foo> separately, there is no guarantee that all IFoo elements in the list do so. So the operation is not safe.

    In order to be able to sort the elements using IComparable<IFoo>, the IFoo interface needs to implement the interface itself.


    Alternatively, you could also implement a IComparer<IFoo> and pass that to Sort() which then delegates to the respective actual implementation. Of course, this is not really an elegant solution and not very future proof (if you ever create a new implementation of IFoo):

    class FooComparer : IComparer<IFoo>
    {
        public int Compare(IFoo a, IFoo b)
        {
            if (a is SomeFoo)
                return ((SomeFoo)a).CompareTo(b);
            else if (a is SomeBar)
                return ((SomeBar)a).CompareTo(b);
            else
                throw new NotImplementedException("Comparing neither SomeFoo nor SomeBar");
        }
    }
    

    Of course, if you mean IFoo to be comparable, you should have that interface implement IComparable<IFoo> directly instead of relying on subtypes to do so. IFoo is a contract, and being sortable is a fine property to require.