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

Jan*_*Jan 8 c# generics

当我有两个非通用Type对象ab,我很容易验证是否是可分配的从b.通过使用所述a.IsAssignableFrom(b)功能.

class ClassBase { }
class ClassDerived : ClassBase { }
...
typeof(ClassBase).IsAssignableFrom(typeof(ClassDerived)) //returns true
Run Code Online (Sandbox Code Playgroud)

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

interface IBase<T> { }
interface IDerived<T> : IBase<T> { }
Run Code Online (Sandbox Code Playgroud)

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

typeof(IBase<object>).IsAssignableFrom(typeof(IDerived<object>)) //returns true
Run Code Online (Sandbox Code Playgroud)

事实上,任何T可用于关闭的东西IDerived也可用于关闭IBase并可IBase<T>IDerived<T>(对于特定的T)分配.

然而,

typeof(IBase<>).IsAssignableFrom(typeof(IDerived<>)) //returns false
Run Code Online (Sandbox Code Playgroud)

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

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

另一种方法是启动实现/继承树b并尝试将其与之比较a.

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

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

Jos*_*nig 1

您提供的通用接口直接从另一个通用继承的示例掩盖了解决开放通用之间兼容性的复杂性。有关更多信息,请阅读 Eric Lippert 最近的博客文章:为什么不自动推断约束?

遵循链接文章中 Eric 的注释,我不会尝试解决确定通用接口是否在所有情况下都可以相互分配的一般情况。该解决方案的很大一部分需要确定这两种类型的约束(如果有)是否完全相交。当一个开放通用接口在某些情况下可分配给另一个开放通用接口但在其他情况下不能分配时,您还必须决定您希望假设的方法返回什么,如果存在重叠但不重合的约束,就会发生这种情况。

更新

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

typeof(Base<>) == typeof(Derived<>).BaseType; // Returns false
typeof(IBase<>) == typeof(Base<>).GetInterfaces()[0]; // Returns false
Run Code Online (Sandbox Code Playgroud)

这可能源于以下事实:开放泛型类型从属性中检索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;
    }
}
Run Code Online (Sandbox Code Playgroud)

给定以下开放通用接口:

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> { }
}
Run Code Online (Sandbox Code Playgroud)

以下所有内容都将返回true

typeof(IBase<>).OpenIsAssignableFrom(typeof(Base<>));
typeof(IBase<>).OpenIsAssignableFrom(typeof(Derived2<>));
typeof(Base<>).OpenIsAssignableFrom(typeof(Derived2<>));
typeof(IBase<>).OpenIsAssignableFrom(typeof(IDerived2<>));
Run Code Online (Sandbox Code Playgroud)

使用构造泛型类型和内置 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>));
Run Code Online (Sandbox Code Playgroud)