8

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

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可以用来关闭的东西IDerived也可以用来关闭IBase并且可以从(对于那个特定的T)IBase<T>分配。IDerived<T>

然而,

typeof(IBase<>).IsAssignableFrom(typeof(IDerived<>)) //returns false

我有点知道为什么会这样(它们可以在不同的类型上关闭,从而变得不可转换?)。我知道在这种情况下确实返回 true 的函数因此有些不同。问题是:“对于每个有效的 都可以IBase<T>分配吗?” (感谢hvdIDerived<T>T

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

另一种方法是在实现/继承树上向上b并尝试将其与a.

我的问题是,即使在一般情况下,是否有更简单的方法可以做到这一点。

动机:普遍的兴趣,因为我实际上并不需要这个。然而,当我将 Ninject 与开放和封闭的泛型一起使用时,最初需要这样做,我需要解决是否可以将开放的泛型类转换为开放的泛型接口(类)。

4

3 回答 3

1

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

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

更新

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

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

这可能源于开放的泛型类型检索自BaseTypeGetInterfaces()具有空FullName属性的事实,即使填充了NamespaceandName属性。因此我也通过自己的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>));
于 2012-08-06T14:31:24.530 回答
1

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

我的问题是,即使在一般情况下,是否有更简单的方法可以做到这一点。

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

如果对于您正在检查可分配性的两种泛型类型中的任何一种T都没有任何约束 ( where T: ...),我认为您可以通过使用objectas 类型参数来构造封闭的泛型类型,然后IsAssignableFrom在构造类型上使用。

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

这里有些例子:

    public class A<T> {}
    public class B<T> : A<T> {}

    public class C<T> where T: E {}
    public class D<T> : C<T> where T: F {}

    public class E {}
    public class F : E {}
    public class G : F {}

    typeof(A<>).IsAssignableFrom(typeof(B<>))              // false
    typeof(A<object>).IsAssignableFrom(typeof(B<object>))  // true
    typeof(A<string>).IsAssignableFrom(typeof(B<string>))  // true
    typeof(C<E>).IsAssignableFrom(typeof(D<F>))            // false
    typeof(C<F>).IsAssignableFrom(typeof(D<F>))            // true
    typeof(C<G>).IsAssignableFrom(typeof(D<G>))            // true
于 2012-08-06T14:42:21.840 回答
0

一个老问题,但由于我在搜索中遇到它并想出了(恕我直言)一个更好的答案,所以我将把它留在这里......

BaseType由于 Joshua 指出的原因,从返回的开放泛型类型上的属性GetGenericTypeDefinition将不可比较。

但是,封闭泛型类型的BaseType属性将返回基的封闭泛型类型。所以你可以沿着封闭的泛型树向上走,直到有一个匹配的 open GetGenericTypeDefinition

bool InheritsOpenGeneric(Type type, Type openGenericType)
{
   if (!type.IsGenericType) return false;
   if (type.GetGenericTypeDefinition() == openGenericType) return true;
   return InheritsOpenGeneric(type.BaseType, openGenericType);
}
于 2015-11-18T20:37:22.233 回答