在C#中打开泛型类继承动态识别

时间:2012-08-06 10:42:55

标签: c# generics

当我有两个非通用Type对象ab时,我可以使用a.IsAssignableFrom(b)函数轻松验证是否可以从b分配a。

class ClassBase { }
class ClassDerived : ClassBase { }
...
typeof(ClassBase).IsAssignableFrom(typeof(ClassDerived)) //returns true

现在说我有两个通用接口:

interface IBase<T> { }
interface IDerived<T> : IBase<T> { }

如果我关闭它们,我可以像以前一样做同样的行为,例如。

typeof(IBase<object>).IsAssignableFrom(typeof(IDerived<object>)) //returns true

事实上,任何可用于关闭T的{​​{1}}也可用于关闭IDerivedIBase可从IBase<T>转让(对于那个特定的T)。

然而,

IDerived<T>

我有点想知道为什么会这样(他们可以关闭不同的类型,从而变得无法转换?)。据我所知,在这种情况下确实返回true的函数有些不同。问题是:“typeof(IBase<>).IsAssignableFrom(typeof(IDerived<>)) //returns false 是否可以为每个有效的IBase<T>分配IDerived<T>?” (感谢hvd

我想做什么正在关闭泛型,然后询问它们是否可分配。但总的来说,我需要在T可以采取的最通用类型下关闭,这可能是a)丑陋,b)很难做到。

另一种方法是在b上执行/继承树,并尝试将其与b进行比较。

我的问题是指在一般情况下是否有更简单的方法。

动机:普遍感兴趣,因为我最后并不需要这个。但是,当我使用Ninject和开放和封闭的泛型时,最初需要这个,我需要解决是否可以将开放的泛型类强制转换为开放的泛型接口(类)。

3 个答案:

答案 0 :(得分:1)

您提供的直接从另一个泛型继承的通用接口的示例掩盖了解决开放泛型之间兼容性的复杂性。有关更多信息,请阅读Eric Lippert最近的博客文章:Why not automatically infer constraints?

在链接文章中推迟到Eric的注释,我不会尝试解决在所有情况下确定通用接口是否可以彼此分配的一般情况。该解决方案的主要部分将需要确定两种类型的约束(如果有的话)是否相交。当一个开放的通用接口在某些情况下可以分配给另一个而不是其他情况时,你还必须决定你想要你的假设方法返回什么,如果有重叠但不重合的约束会发生这种情况。

<强>更新

如果您使用扩展方法将其打包,那么如您所建议的那样直接继承“走在树上”非常容易。但是,为了实际确定两个开放泛型类型是否相等,您必须定义自己的比较,因为内置的相等比较不适用于GetInterfacesBaseType所调用的类型定义通用类型:

typeof(Base<>) == typeof(Derived<>).BaseType; // Returns false
typeof(IBase<>) == typeof(Base<>).GetInterfaces()[0]; // Returns false

这可能源于这样一个事实,即从BaseTypeGetInterfaces()检索到的开放泛型类型具有空FullName属性,即使NamespaceName属性也是如此人口稠密。我也可以通过自己的GetFullName()扩展方法定义,并使用可选的strongName参数来确定是否包含完整的程序集名称。

因此,这里是用于比较开放泛型类型之间的直接继承或实现的相当紧凑的实现:

public static class TypeExtensions {
    public static bool OpenIsAssignableFrom(this Type baseType, Type c, bool strongName = true) {
        if (!baseType.IsGenericTypeDefinition || !c.IsGenericTypeDefinition) return false;
        if (baseType.IsInterface)
            return c.ImplementsOpenInterface(baseType);
        Type testBaseType = c;
        while (testBaseType != null) {
            if (baseType.GetFullName(strongName) == testBaseType.GetFullName(strongName)) return true;
            testBaseType = testBaseType.BaseType;
        }
        return false;
    }

    public static bool ImplementsOpenInterface(this Type sourceType, Type ifaceType, bool strongName = true) {
        if (!ifaceType.IsInterface) return false;
        return sourceType.GetInterfaces().Any(I => I.GetFullName(strongName) == ifaceType.GetFullName(strongName));
    }

    public static string GetFullName(this Type type, bool strongName = false) {
        string name = type.FullName ?? "";
        if (name.Length == 0)
            name = type.Namespace + "." + type.Name;
        if (strongName)
            name += ", " + type.Assembly.FullName;
        return name;
    }
}

给出以下开放通用接口:

namespace TypeExample {
    public interface IBase<T> { }
    public interface IDerived<T> : IBase<T> { }
    public interface IDerived2<T> : IDerived<T> { }

    public class Base<T> : IBase<T> { }
    public class Derived<T> : Base<T>, IDerived<T> { }
    public class Derived2<T> : Derived<T>, IDerived2<T> { }
}

以下所有内容都将返回true

typeof(IBase<>).OpenIsAssignableFrom(typeof(Base<>));
typeof(IBase<>).OpenIsAssignableFrom(typeof(Derived2<>));
typeof(Base<>).OpenIsAssignableFrom(typeof(Derived2<>));
typeof(IBase<>).OpenIsAssignableFrom(typeof(IDerived2<>));

使用构造的泛型类型和内置的IsAssignableFrom,以下是与原始相同的结果:

typeof(IBase<string>).IsAssignableFrom(typeof(Base<string>));
typeof(IBase<string>).IsAssignableFrom(typeof(Derived2<string>));
typeof(Base<string>).IsAssignableFrom(typeof(Derived2<string>));
typeof(IBase<string>).IsAssignableFrom(typeof(IDerived2<string>));

答案 1 :(得分:1)

正如您已经发现的那样,typeof(IBase<>).IsAssignableFrom(typeof(IDerived<>))将永远不会返回true,因为两个开放的泛型类型不在每个其他继承层次结构中。

  

我的问题是,即使在一般情况下,是否有更简单的方法。

不,不是更容易,但是......

如果T对于两种泛型类型中的任何一种没有任何约束(where T: ...),那么您正在检查可分配性,我认为您可以使用{来构造封闭的泛型类型{1}}作为类型参数,然后在构造的类型上使用object

如果IsAssignableFrom受限于任何泛型类型,则必须使用反射来查找这些约束(Type.GetGenericArgumentsType.GetGenericParameterConstraints),然后使用该约束构造泛型类型信息。在这种情况下,由于T继承(相同的A<T> : B<T>),约束类型必须仍然相同,以便在两种泛型类型之间具有可分配性。请注意,如果一个约束类型继承另一个约束类型,那么如果使用两个约束类型中派生最多的类型构造它们,您将发现泛型类型的可赋值性。

以下是一些例子:

T

答案 2 :(得分:0)

一个古老的问题,但是因为我在搜索中遇到它并提出了(恕我直言)一个更好的答案我会把它留在这里......

BaseType返回的开放泛型类型的GetGenericTypeDefinition属性由于Joshua注明的原因而无法比较。

但是,封闭泛型类型的BaseType属性将返回基类的封闭泛型类型。因此,您可以走近封闭的泛型树,直到其中一个匹配打开GetGenericTypeDefinition

bool InheritsOpenGeneric(Type type, Type openGenericType)
{
   if (!type.IsGenericType) return false;
   if (type.GetGenericTypeDefinition() == openGenericType) return true;
   return InheritsOpenGeneric(type.BaseType, openGenericType);
}