【问题标题】:How to find the smallest assignable type in two types (duplicate)? [duplicate]如何在两种类型(重复)中找到最小的可分配类型? [复制]
【发布时间】:2012-12-15 23:19:51
【问题描述】:

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

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 不存在,FindInterfaceWith 无法实现

【问题讨论】:

标签: c# reflection types


【解决方案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&lt;T&gt; 的接口都会在其他接口之前被选中,我认为不正确

FindInterfaceWith 的开放式问题

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

接口和类层次结构

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

Review of this answer at codereview.stackexchange.com

ps
提供完整资源 [here]

【讨论】:

  • FindEqualTypeWith 已添加。您能否提供更多您将使用的类层次结构示例?
  • 如果你能提供一些输入作为断言会很棒
  • 我认为您对FindInterfaceWith 的实施可能会误导您。
  • 对不起,我的私人心灵感应师现在正在放寒假,因此请提供most suitable的确切定义。在有可能设计实现多个接口的类之前,您无法解决此问题。我的建议,FindEqualTypeWith 应该返回几种最佳类型,即枚举类型而不是一种类型。在原始答案中查看FindInterfaceWith 的未决问题。
  • 我已将此答案发送至review at Code Review forum。可能有人可以改进它
【解决方案2】:

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

【讨论】:

    【解决方案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;
    

    }

    希望对你有帮助。

    【讨论】:

      【解决方案4】:

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

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

      您最感兴趣的方法是 public static Tuple&lt;Type, IEnumerable&lt;Type&gt;&gt; GetCommonBases(Type left, Type right)

      返回的元组是

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

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

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

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

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

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

        李>
      
      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-06
      • 2018-04-17
      • 2023-03-24
      • 2021-08-13
      • 2018-10-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-03
      • 2021-09-05
      相关资源
      最近更新 更多