Class and Interface comparing c#

Viewed 594

I want to sort the list of classes and interfaces. So my logic is

  • if the class is not implemented interface it's higher than the interface, otherwise, it's lower

I am using the IComparer interface to sort my list. My model looks like this:

Model

My Comparer class (if returns 1 it means y>x, 0 is x==y -1 is x>y):

public class SortedTypeComparer : IComparer<Type>
{
    /// <summary>
    /// Compares types
    /// </summary>
    public int Compare(Type x, Type y)
    {
        public int Compare(Type x, Type y)
    {
        if (y.IsAssignableFrom(x))
        {
            return 1; 
        }
        else if (x.IsAssignableFrom(y))
        {
            return -1;
        }
        else if (!y.IsAssignableFrom(x) && !x.IsAssignableFrom(y) && x.IsInterface && y.IsClass)
        {
            return 1;
        }
        else if (!y.IsAssignableFrom(x) && !x.IsAssignableFrom(y) && x.IsClass && y.IsInterface)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }
    }
}

I am expecting when I sort the list it should be:

IAnimal
IRunnable
Animal
ICat
Cat

Or:

IRunnable
IAnimal
Animal
ICat
Cat

Because IRunnable and IAnimal are 'equal'. Here is my usage:

     var list = new List<Type>();
        list.Add(typeof(IAnimal));
        list.Add(typeof(IRunnable));
        list.Add(typeof(ICat));
        list.Add(typeof(Animal));
        list.Add(typeof(Cat));
        list.Sort(new SortedTypeComparer());

In this case, it is working as expected. But when I change the order of adding to list for example(put IRunnable to the end):

    var list = new List<Type>();
        list.Add(typeof(IAnimal));
        list.Add(typeof(ICat));
        list.Add(typeof(Animal));
        list.Add(typeof(Cat));
        list.Add(typeof(IRunnable));
        list.Sort(new SortedTypeComparer());

The order is

IAnimal
Animal
ICat
IRunnable
Cat

It is not my expectation because of IRunnable>Animal. It seems when it compare Animal and ICat Animal is higher, then when it compares ICat and IRunnable it's saying "ICat == IRunnable, so Animal should be > IRunnable". How can I write the logic in the Compare method to sort my list as expected?

2 Answers

I do not think this is possible with a IComparer. From CompareTo

  • For objects A, B, and C, the following must be true:
  • If A.CompareTo(B) returns zero and B.CompareTo(C) returns zero, then A.CompareTo(C) is required to return zero.

So if A inherits from C and B does not inherit anything, then according to your rules compareTo should return:

  • A.CompareTo(B) -> 0
  • B.CompareTo(C) -> 0
  • A.CompareTo(C) -> 1

This violates the requirements of CompareTo.

An alternative would be to build an Directed acyclic graph of the hierarchy. Then you should be able to use Topological sorting to sort the graph.

Maybe answer of @JonasH more correct, but I did by fixing My comparer class. I added the following condition in my method and it is correct for most scenarios:

public class SortedTypeComparer : IComparer<Type>
{
    public List<Type> AllTypes { get; set; }

    public SortedTypeComparer(List<Type> types)
    {
        AllTypes = types;
    }

    /// <summary>
    /// Compares types
    /// </summary>
    public int Compare(Type x, Type y)
    {
        var result = CompareIsHigherOrLower(x, y);
        if (result == 0)
        {
            var subEntitiesOfX = AllTypes.Where(a => x.IsAssignableFrom(a) && a != x);
            foreach (var subTypeOfX in subEntitiesOfX)
            {
                result = CompareIsHigherOrLower(subTypeOfX, y);
                if (result == -1)
                {
                    return -1;//It means SubEntity of X is higher then Y and X should be > Y
                }
            }

            var subEntitiesOfY = AllTypes.Where(a => y.IsAssignableFrom(a) && a != y);
            foreach (var subType in subEntitiesOfY)
            {
                result = CompareIsHigherOrLower(subType, x);
                if (result == -1)
                {
                    return 1;//It means SubEntity of Y is higher then X and Y should be > X
                }
            }
        }
        return result;
    }

    int CompareIsHigherOrLower(Type x, Type y)
    {
        if (y.IsAssignableFrom(x))
        {
            return 1;
        }
        else if (x.IsAssignableFrom(y))
        {
            return -1;
        }
        else if (!y.IsAssignableFrom(x) && !x.IsAssignableFrom(y) && x.IsInterface && y.IsClass)
        {
            return 1;
        }
        else if (!y.IsAssignableFrom(x) && !x.IsAssignableFrom(y) && x.IsClass && y.IsInterface)
        {
            return -1;
        }
        else
        {
            return 0;
        }
    }
}
Related