如何找到两种类型之间,以获得最佳的最小协变型?(How to find the minimum co

2019-07-18 13:41发布

IsAssignableFrom方法返回一个布尔值指示是否一种类型是从另一种类型的分配。

怎能不只是如果他们是分配或对方,但也知道,以获得最佳的最小协变型式试验?

考虑下面的例子(C#4.0)

  •  // method body of Func is irrelevant, use default() instead Func<char[]> x = default(Func<char[]>); Func<int[]> y = default(Func<int[]>); Func<Array> f = default(Func<Array>); Func<IList> g = default(Func<IList>); g=x; g=y; y=x; // won't compile x=y; // won't compile // following two are okay; Array is the type for the covariance f=x; // Array > char[] -> Func<Array> > Func<char[]> f=y; // Array > int[] -> Func<Array> > Func<int[]> // following two are okay; IList is the interface for the covariance g=x; g=y; 

在上面的例子,有什么发现是间的类型char[]int[]

Answer 1:

更新:

原来FindInterfaceWith可以被简化,并建立一个扁平化类型作为基类不一定参与,只要我们考虑到的类型本身,当它是一个接口层次结构变得多余; 所以我添加一个扩展方法GetInterfaces(bool) 。 既然我们可以通过覆盖规则的interaces排序,接口的排序交集是候选人。 如果所有的人都一样好,我说他们没有被认为是最好的之一。 如果不是的话,那么最好的一个必须覆盖别人的一个; 因为他们进行排序,这种关系应在数组中最右边的两个接口的存在是为了表示,有一个共同的最好的接口,是最具体的。


该代码可以通过使用简化Linq ; 但在我的情况,我应该减少引用和命名空间尽可能的要求..

  •  using System; public static class TypeExtensions { static int CountOverlapped<T>(T[] ax, T[] ay) { return IntersectPreserveOrder(ay, ax).Length; } static int CountOccurrence(Type[] ax, Type ty) { var a = Array.FindAll(ax, x => Array.Exists(x.GetInterfaces(), tx => tx.Equals(ty))); return a.Length; } static Comparison<Type> GetCoverageComparison(Type[] az) { return (tx, ty) => { int overlapped, occurrence; var ay = ty.GetInterfaces(); var ax = tx.GetInterfaces(); if(0!=(overlapped=CountOverlapped(az, ax).CompareTo(CountOverlapped(az, ay)))) { return overlapped; } if(0!=(occurrence=CountOccurrence(az, tx).CompareTo(CountOccurrence(az, ty)))) { return occurrence; } return 0; }; } static T[] IntersectPreserveOrder<T>(T[] ax, T[] ay) { return Array.FindAll(ax, x => Array.FindIndex(ay, y => y.Equals(x))>=0); } /* static T[] SubtractPreserveOrder<T>(T[] ax, T[] ay) { return Array.FindAll(ax, x => Array.FindIndex(ay, y => y.Equals(x))<0); } static Type[] GetTypesArray(Type typeNode) { if(null==typeNode) { return Type.EmptyTypes; } var baseArray = GetTypesArray(typeNode.BaseType); var interfaces = SubtractPreserveOrder(typeNode.GetInterfaces(), baseArray); var index = interfaces.Length+baseArray.Length; var typeArray = new Type[1+index]; typeArray[index]=typeNode; Array.Sort(interfaces, GetCoverageComparison(interfaces)); Array.Copy(interfaces, 0, typeArray, index-interfaces.Length, interfaces.Length); Array.Copy(baseArray, typeArray, baseArray.Length); return typeArray; } */ public static Type[] GetInterfaces(this Type x, bool includeThis) { var a = x.GetInterfaces(); if(includeThis&&x.IsInterface) { Array.Resize(ref a, 1+a.Length); a[a.Length-1]=x; } return a; } public static Type FindInterfaceWith(this Type type1, Type type2) { var ay = type2.GetInterfaces(true); var ax = type1.GetInterfaces(true); var types = IntersectPreserveOrder(ax, ay); if(types.Length<1) { return null; } Array.Sort(types, GetCoverageComparison(types)); var type3 = types[types.Length-1]; if(types.Length<2) { return type3; } var type4 = types[types.Length-2]; return Array.Exists(type3.GetInterfaces(), x => x.Equals(type4)) ? type3 : null; } public static Type FindBaseClassWith(this Type type1, Type type2) { if(null==type1) { return type2; } if(null==type2) { return type1; } for(var type4 = type2; null!=type4; type4=type4.BaseType) { for(var type3 = type1; null!=type3; type3=type3.BaseType) { if(type4==type3) { return type4; } } } return null; } public static Type FindAssignableWith(this Type type1, Type type2) { var baseClass = type2.FindBaseClassWith(type1); if(null==baseClass||typeof(object)==baseClass) { var @interface = type2.FindInterfaceWith(type1); if(null!=@interface) { return @interface; } } return baseClass; } } 

这里有两种递归方法; 一个是FindInterfaceWith ,另一个是一个重要的方法GetTypesArray因为已经有一个名为方法GetTypeArray类的Type有不同的用法。

它是这样工作的方法阿基姆提供GetClassHierarchy ; 但在这个版本中,它建立像一个数组:

  • 层次结构的输出

     a[8]=System.String a[7]=System.Collections.Generic.IEnumerable`1[System.Char] a[6]=System.Collections.IEnumerable a[5]=System.ICloneable a[4]=System.IComparable a[3]=System.IConvertible a[2]=System.IEquatable`1[System.String] a[1]=System.IComparable`1[System.String] a[0]=System.Object 

我们大家都知道,他们是在一个特定的顺序,这是它怎么做事情的工作。 该阵列GetTypesArray内置实际上是一个扁平化的树。 所述阵列实际上是在模型中作为以下内容:

  • 注意的一些接口实现,诸如关系IList<int>实现ICollection<int>不与该图中线连接起来的。

返回阵列中的接口被排序Array.Sort与由所提供的排序规则GetCoverageComparison

有一些事情提,例如, 多接口的可能性已经落实在一些答案中提到,不仅一次(如[ 这 ]); 我已经定义了解决这些问题的方式,这些都是:

  • 注意

    1. 该GetInterfaces方法不以特定顺序返回接口,如字母或声明顺序。 您的代码一定不能依赖其返回接口的顺序,因为该顺序变化。

    2. 由于递归的,基类总是有序的。

    3. 如果两个接口有相同的覆盖范围,都没有将被视为符合条件。

      假设我们有定义这些接口(或类就好了):

       public interface IDelta { } public interface ICharlie { } public interface IBravo: IDelta, ICharlie { } public interface IAlpha: IDelta, ICharlie { } 

      那么哪一个是对的分配更好IAlphaIBravo ? 在这种情况下, FindInterfaceWith只是返回null

在这个问题[ 如何找到两种类型(重复)的最小分配的类型? ],我说:

  • 错扣

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

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

然而,现在我们可以看一下方法FindAssignableWith ,它必须调用其它两种方法是基于原假设,矛盾的错误神奇地消失了。


有关订购界面,在委托的范围比较规则GetCoverageComparison ,我使用:

  • 双重规则

    1. 在源接口阵列比较两个接口,在源,每个覆盖有多少人,通过调用CountOverlapped

    2. 如果规则1不区分它们(返回0 ),第二顺序是调用CountOccurrence以确定哪些已被他人继承更多次,然后比较

      这两个规则等同于Linq查询:

       interfaces=( from it in interfaces let order1=it.GetInterfaces().Intersect(interfaces).Count() let order2=( from x in interfaces where x.GetInterfaces().Contains(it) select x ).Count() orderby order1, order2 select it ).ToArray(); 

      FindInterfaceWith然后将执行可能递归调用,搞清楚的是这个接口足以公认的最常用的接口还是像刚才另一个关系IAlphaIBravo

而有关该方法的FindBaseClassWith ,它返回的是从如果任何参数为null,则返回null原来的假定不同。 它实际上返回传入另一种说法。

这涉及到的问题[ 应该采取什么方法`FindBaseClassWith`返回? ]关于方法链接FindBaseClassWith 。 在目前的实现,我们可以调用它像:

  • 方法链接

     var type= typeof(int[]) .FindBaseClassWith(null) .FindBaseClassWith(null) .FindBaseClassWith(typeof(char[])); 

    它将返回typeof(Array) ; 感谢这个功能,我们甚至可以称之为

     var type= typeof(String) .FindAssignableWith(null) .FindAssignableWith(null) .FindAssignableWith(typeof(String)); 

    我们可能不能够与我的实现做的是调用FindInterfaceWith像上面,因为像关系的可能性IAlphaIBravo

我已经通过调用某些情况下测试的代码FindAssignableWith如图所示的例子:

  • 分配类型的输出

     (Dictionary`2, Dictionary`2) = Dictionary`2 (List`1, List`1) = IList (Dictionary`2, KeyValuePair`2) = Object (IAlpha, IBravo) = <null> (IBravo, IAlpha) = <null> (ICollection, IList) = ICollection (IList, ICollection) = ICollection (Char[], Int32[]) = IList (Int32[], Char[]) = IList (IEnumerable`1, IEnumerable`1) = IEnumerable (String, Array) = Object (Array, String) = Object (Char[], Int32[]) = IList (Form, SplitContainer) = ContainerControl (SplitContainer, Form) = ContainerControl 

    所述List'1试验出现IList是因为我测试typeof(List<int>)typeof(List<String>) ; 和Dictionary'2均为Dictionary<String, String> 。 对不起,我没有做的工作提出确切的类型名称。



Answer 2:

最简单的情况是在基类型一个对象,并检查它们被分配与其它类型的,像这样的迭代:

  •  public Type GetClosestType(Type a, Type b) { var t=a; while(a!=null) { if(a.IsAssignableFrom(b)) return a; a=a.BaseType; } return null; } 

这将产生System.Object两种类型的是不相关的,如果他们都是类。 我不知道,如果这种行为满足您的要求。

对于更高级的情况下,我使用的是所谓的自定义扩展方法IsExtendablyAssignableFrom

它可以处理不同的数字类型,泛型接口,泛型参数,隐式转换,空,装箱/拆箱,和几乎所有我所遇到的实施我自己的编译器在与类型。

我上传的代码到一个单独的GitHub库[ 这里 ],所以你可以在你的项目中使用它。



Answer 3:

如果你看一下基类而已 ,问题是琐碎的解决方案是通过Impworks的回答给定(“遍历一个对象的父母,并检查他们被分配与其他类型的”)。

但是如果你想也包括接口,有没有独特的解决问题的办法,因为你注意到自己与你的IDeltaICharlie例子。 两个或多个接口,可以很容易地同样“好”,所以没有单一的最佳解决方案。 人们可以很容易构造接口继承的任意复杂的图(曲线图),并且可以很容易地从这些图中看到,有没有明确定义的“FindAssignableWith”。

此外,在C#中的协方差/逆变用于方差各类通用类型。 让我举个例子。 Supose我们

type1: System.Func<string>
type2: System.Func<Tuple<int>>

然后当然与基类中,“FindAssignableWith”可能是

solutionA: System.MulticastDelegate

但类型Func<out T>协变out在其类型参数) T 。 因此,类型

solutionB: System.Func<System.Object>

也是在这个意义上的解决方案,它IsAssignableFrom两个给定类型type1type2 。 但同样可以说的

solutionC: System.Func<System.IComparable>

其可行的,因为这两个stringTuple<>IComparable

所以在一般情况下,有没有独特的解决方案。 所以,除非你指定精确的规则描述你想要的东西,我们不能拿出那找到解决方案的算法。



文章来源: How to find the minimum covariant type for best fit between two types?