Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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);
}
share|improve this question
    
I've awarded the bounty to you on S.O. Thanks a lot!! –  Ken Kin Jan 10 '13 at 10:13
1  
Seems SE is a quiet and peaceful place. –  Ken Kin Jan 15 '13 at 11:40
    
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 –  almaz Jan 17 '13 at 8:58
    
This is an answer for question on StackOverflow. If you've any comments regarding implemetation, you're welcome –  Akim Jan 17 '13 at 9:34
1  
"I'm not sure that this is correct implementation in case of complex class hierarhy with many implemented interfaces". Please define correct. –  Jean Hominal Jan 21 '13 at 10:25

2 Answers 2

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.

share|improve this answer

update:

This question can be solved by the answer of related question


I've voted up the answer by Jean Hominal. The descriptions in that answer is true. Alternatively, let we change the method FinBaseClassWith to use the BaseType directly provided by the framework:

public static Type FindBaseClassWith(this Type type1, Type type2) {
    for(var baseType2=type2; null!=baseType2; baseType2=baseType2.BaseType)
        for(var baseType1=type1; null!=baseType1; baseType1=baseType1.BaseType)
            if(baseType2==baseType1)
                return baseType2;

    return default(Type);
}

consider you have two arrays and you are going to assign one to another. And you test them with:

objectArray.GetType().FindBaseTypeWith(charArray.GetType()); 

and

charArray.GetType().FindBaseTypeWith(objectArray.GetType()); 

They are all typeof(Array), but it's not possible to do

charArray=objectArray;

And copying with Array.Copy throws an exception. I cannot figure out what's the difference even though they have the same BaseType.

The original question I asked on S.O., is relevent of to find the possible path of assignment, before we invoke the constructor of the target type.

share|improve this answer
1  
Why would you be able to assign a value of type A to a variable of type B by virtue of A and B sharing a common type C? About the only thing that means for assignment is that values of type A and B can be assigned to variables of type C. That is, both char[] and object[] can be affected to variables of type Array. Array.Copy is only partly related, and the results of it will depend a lot on both the target type, the destination type, and the contents of the source array. –  Jean Hominal Jan 22 '13 at 1:00
    
If you have .Net Reflector installed, I suggest that to take a look at Type.IsAssignableFrom. However, it returns only a boolean result. In my scenario there's more restriction, even cannot use of Linq except IEnumerable and IEnumerator. A boolean value is not helpful enough to accomplish the work afterwards. –  Ken Kin Jan 22 '13 at 1:49

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.