这是两种使用的扩展方法
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?
}
反射和Linq仅限使用,除非没有别的办法。
是否有很好的方法可以在type1
和type2
之间找到最适合的常见类型?
还是有更好的方法来实现这一目标吗?
更新:
根据我个人的理解,由于 使用类实现多个接口 的能力,FindInterfaceWith
可能需要调用FindBaseClassWith
内部;否则最好的选择类型将是不可判定的。
如果这个假设是正确的,那么FindInterfaceWith
就成了一个冗余的方法;因为FindInterfaceWith
和FindAssignableWith
之间的唯一区别是:
FindInterfaceWith
会返回null
;而FindAssignableWith
会直接返回确切的类。
否则,它们都会返回最佳的界面选择。
这是关于说最初的假设是不合理的。也就是说,如果FindInterfaceWith
不是 ,则 FindAssignableWith
无法实施。
答案 0 :(得分:10)
这是我的实施:
FindAssignableWith
,FindBaseClassWith
和FindInterfaceWith
实施// 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
实施任何实现IEnumerable
或IEnumerable<T>
的接口都将在其他接口之前被选中,我认为不正确
FindInterfaceWith
c#允许在一个类中实现多个接口,在这种情况下,第一个接口将由FindInterfaceWith
,返回,因为无法知道哪个接口{在以下示例
IA
通常更受欢迎
IB
使用 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
Review of this answer at codereview.stackexchange.com
ps :
完整资源[here]
答案 1 :(得分:2)
哦,是的,我可以炫耀一下我最近写的东西! :)
警告:这段代码不是世界上效率最高的,而且评论非常糟糕 - 这是个人项目,我已经知道它是如何工作的 - 但我认为它会让你得到你所追求的...您最感兴趣的方法是
public static Tuple<Type, IEnumerable<Type>> GetCommonBases(Type left, Type right)
返回的元组是&lt;公共基类,(公共接口列表)&gt;
快速摘要:给定类型时,此类执行以下操作:
反向走动给定类型,直到它不再触及基础类型,将每个类型推入“工作堆栈”
将每个基本类型从工作堆栈中弹出,将其插入树状结构中;如果类型实现任何接口,它还为这些接口类型添加节点
辅助方法GetCommonBases
为第一种类型创建这些TypeTree
结构之一,然后在另一种给定类型的类型树中“合并”:它通过向下走基类型,直到它找到一个点,在这两个类型之间有一个公共基类型,此时形成树的两个分支。然后它从根部“向下钻取”到每种类型(即System.Object
),然后找到第一个偏差点。此偏差点的父级是Common基类型。
接口部分依赖于Interfaces
的定义,它为任何祖先“继承”任何接口节点。 GetCommonBases
方法拉出两个传入类型实现的任何接口的列表,并返回这两个列表的交集 - 也就是说,一组接口都传入类型实现。
然后该方法将这两位信息作为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;
}
}
答案 2 :(得分: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;
}
希望它有所帮助。
答案 3 :(得分: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();
}
基本上它在排序中将基类和接口视为相同。
我想基础实现来自[here]。
我所做的基本上是将两种方法粘合在一起,而不改变原始功能的语义。
示例:
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...