8
\$\begingroup\$

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

  1. Searching for common base class (either concrete or abstract)
    • FindBaseClassWith method
  2. 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

  1. 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
  2. Are there any other flaws that I do not pay attention to?
  3. 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 => !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<Type> GetInterfaceHierarchy(this Type type)
{
    if(type.IsInterface) return new [] { type }.AsEnumerable();

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

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

    Type typeInHierarchy = type;

    do
    {
        yield return typeInHierarchy;
        typeInHierarchy = typeInHierarchy.BaseType;
    }
    while(typeInHierarchy != null && !typeInHierarchy.IsInterface);
}
\$\endgroup\$
4
  • 2
    \$\begingroup\$ Seems SE is a quiet and peaceful place. \$\endgroup\$ Commented Jan 15, 2013 at 11:40
  • \$\begingroup\$ What is the question here? Do you want to hear comments on implementation, or poking for a different approach? And the main question - why do you need this functionality? I've never had the need to find common base classes using reflection \$\endgroup\$ Commented Jan 17, 2013 at 8:58
  • \$\begingroup\$ This is an answer for question on StackOverflow. If you've any comments regarding implemetation, you're welcome \$\endgroup\$ Commented Jan 17, 2013 at 9:34
  • 1
    \$\begingroup\$ "I'm not sure that this is correct implementation in case of complex class hierarhy with many implemented interfaces". Please define correct. \$\endgroup\$ Commented Jan 21, 2013 at 10:25

1 Answer 1

5
+25
\$\begingroup\$

Your code does not take in account interface covariance and contravariance issues.

Here is the sample code that demonstrates it:

class ObservableOfString : IObservable<string> { /*...*/ }
class ObservableOfObject : IObservable<object> { /*...*/ }
class ObserverOfObject : IObserver<object> { /*...*/ }
class ObserverOfString : IObserver<string> { /*...*/ }
class Program
{

    static void Main(string[] args)
    {
        IObservable<object> observableIsCovariant = new ObservableOfString();
        IObserver<string> observerIsContravariant = new ObserverOfObject();
        Console.WriteLine(typeof(ObservableOfString).FindEqualTypeWith(typeof(ObservableOfObject)) ?? (object)"<NULL>"); // Would it not be more useful to return IObservable<object>?
        Console.WriteLine(typeof(ObserverOfString).FindEqualTypeWith(typeof(ObserverOfObject)) ?? (object)"<NULL>"); // Would it not be more useful to return IObserver<string>?
        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.

\$\endgroup\$

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.