如何找到两种类型中最小的可分配类型(重复)?

这是两种使用的扩展方法

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 } 
  • 如果FindInterfaceWith没有通用的实现接口,则返回null
  • 如果FindBaseClassWith没有更多的派生公共基类,则返回System.Object
  • 如果其中一个参数是接口,则FindBaseClassWith返回null
  • 如果任何参数为null它们都返回null

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

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

reflection和Linq仅限使用,除非没有其他办法。

有没有很好的方法来找到type1type2之间最常见的常见类型?

或者有更好的事情来实现这一目标吗?


更新:

根据我个人的理解,由于能够使用类实现多个接口FindInterfaceWith可能需要在内部调用FindBaseClassWith ; 否则最好的选择类型将是不可判定的。

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

如果有最佳的类选择, FindInterfaceWith返回null ; FindAssignableWith直接返回确切的类。

否则,它们都会返回最佳的界面选择。

这是关于说最初的假设是不合理的。 也就是说, 如果FindAssignableWith不是则无法实现FindAssignableWith

这是我的实施:

FindAssignableWithFindBaseClassWithFindInterfaceWith实现

 // 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 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 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都将在其他接口之前被选中, 我认为这些接口不正确

FindInterfaceWith开放式问题

c#允许在一个类中实现多个接口,在这种情况下, FindInterfaceWith将返回第一个接口, 因为在以下示例中通常无法知道哪个接口IAIB是优选的

multiple_interfaces_implementing

接口和类层次结构

  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
完整资源[ 这里 ]

哦,是的,我可以炫耀一下我最近写的东西! 🙂

警告:这段代码不是世界上效率最高的,它的评论非常糟糕 – 这是个人项目,我已经知道它是如何工作的 – 但我认为它会让你得到你想要的东西……

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

返回的元组是<公共基类,(公共接口列表)>

快速摘要:此类在给定类型时执行以下操作:

  • 反向向上移动给定类型,直到它不再触及基类型,将每个类型推入“工作堆栈”

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

  • helper方法GetCommonBases为第一个类型创建这些TypeTree结构之一,然后在另一个给定类型的类型树中“合并”:它通过向下走共同基类型直到找到具有公共基类型的点为止在这两种类型之间,形成了树的两个分支。 然后它从根(即System.Object )“向下钻取”到每个类型,然后找到第一个偏差点。 这一偏差点的父级是Common基类型。

  • 接口部分依赖于接口的定义, Interfaces “inheritance”任何祖先的任何接口节点。 GetCommonBases方法提取由两个传入类型实现的任何接口的列表,并返回这两个列表的交集 – 也就是说,一组接口都传入类型实现。

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

 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; } } 

我将有一个默认实现和一些众所周知的类和接口按优先级排序。 在这里我的实施:

 private static List CommonTypesPriorities = new List { 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; 

}

希望能帮助到你。

更新+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(); } 

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

例:

 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...