Evaluate the best common type to fit both of types Here is an algorithm to evaluate best matching System.Type for two types in hierarhy. This is one of answers for this StackOverflow question by Ken Kin:


  Evaluate the maximum possible type to fit both of types


Algorithm


Searching for common base class (either concrete or abstract)


FindBaseClassWith method

If there is no common base class, search for common implemented interface


It's possible for one class to implement multiple interfaces, in this case return first common based interface
FindInterfaceWith method



Questions


GetInterfaceHierarchy implementaion


I'm not sure that this is correct implementation in case of complex class hierarhy with many implemented interfaces
Now all tests are passing for selected collection from BCL and my own testing hierarhy

Are there any other flaws that I do not pay attention to?
I had wrote this sample in LinqPad, that's why all unittests are located at Main method. Nevertheless, any improvments to Test.Assert are ok!


Full source code with unittests is available here

// provide common base class or implemented interface
public static Type FindEqualTypeWith(this Type typeLeft, Type typeRight)
{
    if(typeLeft == null || typeRight == null) return null;

    var commonBaseClass = typeLeft.FindBaseClassWith(typeRight) ?? typeof(object);

    return commonBaseClass.Equals(typeof(object))
            ? typeLeft.FindInterfaceWith(typeRight)
            : commonBaseClass;
}

// searching for common base class (either concrete or abstract)
public static Type FindBaseClassWith(this Type typeLeft, Type typeRight)
{
    if(typeLeft == null || typeRight == null) return null;

    return typeLeft
            .GetClassHierarchy()
            .Intersect(typeRight.GetClassHierarchy())
            .FirstOrDefault(type =&gt; !type.IsInterface);
}

// searching for common implemented interface
// it's possible for one class to implement multiple interfaces, 
// in this case return first common based interface
public static Type FindInterfaceWith(this Type typeLeft, Type typeRight)
{
    if(typeLeft == null || typeRight == null) return null;

    return typeLeft
            .GetInterfaceHierarchy()
            .Intersect(typeRight.GetInterfaceHierarchy())
            .FirstOrDefault();   
}

// iterate on interface hierarhy
public static IEnumerable&lt;Type&gt; GetInterfaceHierarchy(this Type type)
{
    if(type.IsInterface) return new [] { type }.AsEnumerable();

    return type
            .GetInterfaces()
            .OrderByDescending(current =&gt; current.GetInterfaces().Count())
            .AsEnumerable();
}

// interate on class hierarhy
public static IEnumerable&lt;Type&gt; GetClassHierarchy(this Type type)
{
    if(type == null) yield break;

    Type typeInHierarchy = type;

    do
    {
        yield return typeInHierarchy;
        typeInHierarchy = typeInHierarchy.BaseType;
    }
    while(typeInHierarchy != null &amp;&amp; !typeInHierarchy.IsInterface);
}
Your code does not take in account interface covariance and contravariance issues.

Here is the sample code that demonstrates it:

class ObservableOfString : IObservable&lt;string&gt; { /*...*/ }
class ObservableOfObject : IObservable&lt;object&gt; { /*...*/ }
class ObserverOfObject : IObserver&lt;object&gt; { /*...*/ }
class ObserverOfString : IObserver&lt;string&gt; { /*...*/ }
class Program
{

    static void Main(string[] args)
    {
        IObservable&lt;object&gt; observableIsCovariant = new ObservableOfString();
        IObserver&lt;string&gt; observerIsContravariant = new ObserverOfObject();
        Console.WriteLine(typeof(ObservableOfString).FindEqualTypeWith(typeof(ObservableOfObject)) ?? (object)"&lt;NULL&gt;"); // Would it not be more useful to return IObservable&lt;object&gt;?
        Console.WriteLine(typeof(ObserverOfString).FindEqualTypeWith(typeof(ObserverOfObject)) ?? (object)"&lt;NULL&gt;"); // Would it not be more useful to return IObserver&lt;string&gt;?
        Console.ReadLine();
    }
}


The fundamental issue is, as soon as you want to find the "best common interface", you are going to run in an issue, as "best" and "common" are at best dependent on the context. You should clearly identify which use cases you are going to support, in the context of why that method was needed, and write a specific implementation that solves your need.