如何找到两种类型(重复)的最小分配的类型? [重复](How to find the small

2019-07-17 17:31发布

这个问题已经在这里有一个答案:

  • 如何找到两种类型之间,以获得最佳的最小协变型? 3个回答

我这里还有使用两个扩展方法

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被限制使用,但没有其他办法。

有没有好的方法之间找到共同类型的最适合的type1type2

还是有更好的东西来实现这一目标?


更新:

根据我个人的理解,因为实现了一类多个接口能力 ,在FindInterfaceWith可能可能需要调用FindBaseClassWith内部; 否则型的最佳选择是不可判定的。

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

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

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

这大约是说原来的设想是不合理的。 也就是说, FindInterfaceWith不能如果实施FindAssignableWith不是

Answer 1:

下面是我的实现:

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

C#允许在一类中实现多个接口,在这种情况下的接口第一个将被返回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.stackexchange.com

PS:
可以使用完整的源[ 这里 ]



Answer 2:

噢,耶,我可以炫耀的东西我最近写了别的东西! :)

警告:此代码是不是最有效的世界,它是非常糟糕的评论 - 这是一个个人项目,我已经知道它是如何工作 - 但我认为它会得到你之后是什么?

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

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

简要总结:这个类,给定一个类型时,执行以下操作:

  • 逆向走到给定类型,直到遇到没有更多的基本类型,互推到一个“工作组”

  • 弹出每个碱基类型从上述工作栈,其插入到树状结构; 如果类型实现任何接口,它增加了节点的那些接口类型,

  • 辅助方法GetCommonBases创建这些之一TypeTree对于第一种类型的结构,那么“合并”的类型树为其他给定类型:它通过走普通基本类型,直到它找到一个地步,你有一个共同的基本类型这样做这两种类型的,在该点上形成树的两个分支之间。 然后,它“向下钻”到每个类型从根(即, System.Object ),那么发现偏差的第一点。 这一点偏差的母公司是通用基本类型。

  • 接口部分依赖于定义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;
   }

}



Answer 3:

我将有一个默认的实现和一些知名的类和接口按优先级排序的心中有。 在这里我实现:

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;

}

希望能帮助到你。



Answer 4:

更新+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...


文章来源: How to find the smallest assignable type in two types (duplicate)? [duplicate]