13

这里有两种扩展方法供使用

public static Type FindInterfaceWith(this Type type1, Type type2) {
    // returns most suitable common implemented interface
}

public static Type FindBaseClassWith(this Type type1, Type type2) {
    // returns most derivative of common base class
}
  • FindInterfaceWithnull如果它们没有通用的实现接口,则返回。
  • FindBaseClassWithSystem.Object如果它们没有更多派生的公共基类,则返回。
  • FindBaseClassWithnull如果参数之一是接口,则返回。
  • null如果任何参数是 ,它们都会返回null

最终解决方案中方法的签名如下:

public static Type FindAssignableWith(this Type type1, Type type2) {
    // what should be here?
}

Reflection 和 Linq 被限制使用,除了没有其他方法。

type1有没有很好的方法来找到和之间的常见类型的最佳拟合type2

还是有更好的方法来实现这一目标?


更新:

以我个人的理解,由于一个类可以实现多个接口FindInterfaceWith,可能需要在FindBaseClassWith内部调用;否则类型的最佳选择将无法确定。

如果这个假设是正确的,那么就FindInterfaceWith变成了一种多余的方法;FindInterfaceWith因为和之间的唯一区别FindAssignableWith是:

FindInterfaceWithnull如果有最好的班级选择,则返回;whileFindAssignableWith直接返回确切的类。

否则,它们都会返回最佳的接口选择。

这是关于说最初的假设是不合理的。即FindInterfaceWith如果FindAssignableWithis not 则无法实现。

4

4 回答 4

10

这是我的实现:

FindAssignableWith,FindBaseClassWithFindInterfaceWith实现

// provide common base class or implemented interface
public static Type FindAssignableWith(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);
}

FindInterfaceWith关于实施的备注

任何实现IEnumerableIEnumerable<T>将在其他接口之前选择的接口,我认为不正确

开放式问题FindInterfaceWith

允许在一个类中实现多个接口,在这种情况下,第一个接口将由返回FindInterfaceWith因为在以下示例中无法知道哪个接口IAIB通常更可取

多个接口实现

接口和类层次结构

    public interface IBase {}
    public interface ISomething {}
    public interface IDerivied: IBase {}
    public interface IDeriviedRight: IDerivied {}
    public interface IDeriviedLeft: IDerivied, IDisposable {}

    public class AnotherDisposable: IDisposable {
        public void Dispose() {
        }
    }

    public class DeriviedLeft: IDeriviedLeft {
        public void Dispose() {
        }
    }

    public class SubDeriviedLeft: DeriviedLeft {}
    public class SecondSubDeriviedLeft: DeriviedLeft {}
    public class ThirdSubDeriviedLeft: DeriviedLeft, ISomething {}

    public class Another {}
    public class DeriviedRight: IDeriviedRight {}

测试用例

NUnit以及使用断言的一组测试用例:

FindBaseClassWith断言示例

// FindBaseClassWith returns null if one of parameters was an interface. 
// FindBaseClassWith  return null if any of parameter was null. 
Assert.That(typeof(DeriviedLeft).FindBaseClassWith(typeof(DeriviedLeft)), Is.EqualTo(typeof(DeriviedLeft)));

FindInterfaceWith断言示例

// FindInterfaceWith returns null if they don't have common implemented interface. 
// FindBaseClassWith  return null if any of parameter was null. 
Assert.That(typeof(DeriviedLeft).FindInterfaceWith(typeof(DeriviedLeft)), Is.EqualTo(typeof(IDeriviedLeft)));

FinAssignableWith断言示例

Assert.That(typeof(DeriviedLeft).FindAssignableWith(typeof(DeriviedLeft)), Is.SameAs(typeof(DeriviedLeft)));

CodeReview 上的讨论

回顾这个答案codereview.stackexchange.com

ps
完整的资源可用[这里]

于 2013-01-03T11:01:43.763 回答
2

哦,是的,我可以炫耀我最近为别的东西写的东西!:)

警告:这段代码不是世界上最高效的,而且评论很差——它是针对个人项目的,我已经知道它是如何工作的——但我认为它会让你得到你想要的......

您最感兴趣的方法是 public static Tuple<Type, IEnumerable<Type>> GetCommonBases(Type left, Type right)

返回的元组是 <common base class, (list of common interfaces)>

快速总结:这个类,当给定一个类型时,执行以下操作:

  • 反向遍历给定类型,直到它不再遇到基本类型,将每个类型推入“工作堆栈”

  • 从工作堆栈中弹出每个基本类型,将其插入到树状结构中;如果该类型实现了任何接口,它也会为这些接口类型添加节点

  • 辅助方法为第一种类型GetCommonBases创建这些TypeTree结构中的一个,然后在类型树中为另一种给定类型“合并”:它通过遍历常见的基本类型来做到这一点,直到找到一个点,在两种类型,此时形成树的两个分支。然后它从根(即System.Object)“向下钻取”每种类型,然后找到第一个偏差点。这个偏差点的父节点是 Common 基本类型。

  • 接口部分依赖于 的定义Interfaces,它为任何祖先“继承”任何接口节点。该GetCommonBases方法提取由两个传入类型实现的任何接口的列表,并返回这两个列表的交集——即,一组传入类型都实现的接口。

  • 然后该方法将这两位信息作为 a 返回Tuple<Type, IEnumerable<Type>>,其中第一项是公共基本类型(如果有),第二项是公共接口的交集


public class TypeTree
{
   private TypeTree()
   {
       Children = new List();
   }

   public TypeTree(Type value)
       : this()
   {
       // Get to the basest class
       var typeChain = GetTypeChain(value).ToList();
       Value = typeChain.First();
       foreach (var type in typeChain.Skip(1))
       {
           Add(type);
       }
   }

   public Type Value { get; private set; }
   public TypeTree Parent { get; private set; }
   public List Children { get; private set; }
   public IEnumerable Interfaces
   {
       get
       {
           var myInterfaces = Children.Where(c => c.Value.IsInterface);
           return Parent == null ? myInterfaces : myInterfaces.Concat(Parent.Interfaces).Distinct();
       }
   }

   public TypeTree Find(Type type)
   {
       if (Value == type)
           return this;
       return Children.Select(child => child.Find(type)).FirstOrDefault(found => found != null);
   }

   public TypeTree Add(Type type)
   {
       TypeTree retVal = null;
       if (type.IsInterface)
       {
           if (Value.GetInterfaces().Contains(type))
           {
               retVal = new TypeTree { Value = type, Parent = this };
               Children.Add(retVal);
               return retVal;
           }
       }
       var typeChain = GetTypeChain(type);
       var walkTypes =
           from baseType in typeChain
           let alreadyExists = Value == baseType || Children.Any(c => c.Value == baseType)
           where !alreadyExists
           select baseType;
       foreach (var baseType in walkTypes)
       {
           if (baseType.BaseType == Value)
           {
               // Add this as a child of the current tree
               retVal = new TypeTree { Value = baseType, Parent = this };
               Children.Add(retVal);
           }
           if (Value.IsAssignableFrom(baseType))
           {
               // we can add this as a child, potentially
               retVal = Children.Aggregate(retVal, (current, child) => child.Add(baseType) ?? current);
           }
           // add interfaces
           var interfaces = baseType.GetInterfaces().Where(i => i != type);
           foreach (var intType in interfaces)
           {
               (retVal ?? this).Add(intType);
           }
       }
       return retVal;
   }

   public override string ToString()
   {
       var childTypeNames = Children.Select(c => c.ToString()).Distinct();
       return string.Format("({0} {1})", Value.Name, string.Join(" ", childTypeNames));
   }

   public static Tuple> GetCommonBases(Type left, Type right)
   {
       var tree = new TypeTree(left);
       tree.Add(right);
       var findLeft = tree.Find(left);
       var findRight = tree.Find(right);

       var commonInterfaces =
           findLeft.Interfaces.Select(i => i.Value)
           .Intersect(findRight.Interfaces.Select(i => i.Value))
           .Distinct();

       var leftStack = new Stack();
       var temp = findLeft;
       while (temp != null)
       {
           leftStack.Push(temp);
           temp = temp.Parent;
       }
       var rightStack = new Stack();
       temp = findRight;
       while (temp != null)
       {
           rightStack.Push(temp);
           temp = temp.Parent;
       }
       var zippedPaths = leftStack.Zip(rightStack, Tuple.Create);
       var result = zippedPaths.TakeWhile(tup => tup.Item1.Value == tup.Item2.Value).Last();            
       return Tuple.Create(result.Item1.Value, commonInterfaces);
   }

   private static IEnumerable GetTypeChain(Type fromType)
   {
       var typeChain = new Stack();
       var temp = fromType;
       while (temp != null)
       {
           typeChain.Push(temp);
           temp = temp.BaseType;
       }
       return typeChain;
   }

}

于 2013-01-01T09:11:17.683 回答
1

更新+1:现在,没有愚蠢的错误和更多细节

我想这就是你要找的东西:

public static Type FindAssignableWith(this Type typeLeft, Type typeRight) {
    if(typeLeft==null||typeRight==null)
        return null;

    var typeLeftUion=typeLeft.GetInterfaceHierarchy().Union(typeLeft.GetClassHierarchy());
    var typeRightUion=typeRight.GetInterfaceHierarchy().Union(typeRight.GetClassHierarchy());

    return 
        typeLeftUion.Intersect(typeRightUion)
            .OrderByDescending(interfaceInHierarhy => interfaceInHierarhy.GetInterfaces().Contains(typeof(IEnumerable)))
            .ThenByDescending(interfaceInHierarhy => interfaceInHierarhy.Equals(typeof(IEnumerable)))
            .FirstOrDefault();
}

基本上,它在排序上将基类和接口视为相同。
我想基本实现来自[这里]。
我所做的基本上是将这两种方法粘合在一起,而不改变原始功能的语义。

例子:

var result=typeof(char[]).FindAssignableWith2(typeof(string[]));
Console.WriteLine("{0}", typeof(char[]).FindAssignableWith2(typeof(string[]))); // IList
Console.WriteLine("{0}", typeof(Test).FindAssignableWith2(typeof(string[]))); // Object
// and so on...
于 2013-01-09T18:49:05.480 回答
1

我将记住一个默认实现和一些按优先级排序的知名类和接口。这是我的实现:

private static List<Type> CommonTypesPriorities = new List<Type> 
                                       {
                                           typeof(IEnumerable), 
                                           typeof(Array), 
                                           typeof(IClonable)
                                       };

public static Type FindAssignableWith(this Type type1, Type type2)
{
    if(type1 == type2) 
        return type1;

    var baseClass = type1.FindBaseClassWith(type2);

    //if the base class is not object/null and it is not in the list, then return it.
    if(baseClass != typeof(object) && baseClass != null && !CommonTypesPriorities.Contains(type))
        return baseClass;

    var @interface = type1.FindInterfaceWith(type2);

    if(@interface == null)
        return baseClase;

    //if there's no base class and the found interface is not in the list, return it
    if(baseClass != null && !CommonTypesPriorities.Contains(@interface)                         
        return @interface;

    //Now we have some class and interfaces from the list.

    Type type = null;
    int currentPriority;

    //if the base class is in the list, then use it as the first choice
    if(baseClass != null && CommonTypesPriorities.Contains(type))
    {
        type = baseClass;
        currentPriority = CommonTypesPriorities.IndexOf(type);
    }

    var interfaces1 = type1.GetInterfaces();
    var interfaces2 = type2.GetInterfaces();

    foreach(var i in interfaces1)
    {
        if(interfaces2.Contains(i))
        {
            //We found a common interface. Let's check if it has more priority than the current one
            var priority = CommonTypesPriorities.IndexOf(i);
            if(i >= 0 && i < currentPriority)
            {
                currentPriority = priority;
                type = i;
            }
        }
    }

    return type;

}

希望能帮助到你。

于 2013-01-03T15:05:57.123 回答