如何找到两种类型之间最佳拟合的最小协变类型?

时间:2013-01-23 03:45:11

标签: c# types covariance contravariance

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[]之间的类型。

2 个答案:

答案 0 :(得分: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存储库[here],因此您可以在项目中使用它。

答案 1 :(得分:1)

如果你只看基类,那么问题就很简单了,并且Impworks的回答给出了一个解决方案(“迭代一个对象的父级并检查它们是否可以与另一个类型一起分配”)。

但是如果你想要包含接口,那么这个问题没有独特的解决方案,因为你会注意到自己的IDeltaICharlie示例。两个或更多接口可以很容易地同样“好”,因此没有单一的最佳解决方案。人们可以很容易地构造任意复杂的界面继承图(图),从这些图中很容易看出,没有明确定义的“FindAssignableWith”。

此外,C#中的协方差/逆变用于 泛型 类型的方差种类。让我举个例子。我们有了

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

所以在一般情况下,没有独特的解决方案。因此,除非您指定描述所需内容的精确规则,否则我们无法提出找到解决方案的算法。