打开类型List<>
和IEnumerable<>
之间是否存在任何关系?
示例:
var type1 = typeof(List<>);
var type2 = typeof(IEnumerable<>);
//return false
type2.IsAssignableFrom(type1);
是否有任何方法可以检查两个打开类型之间的关系,或者该关系仅在闭合类型上存在?
答案 0 :(得分:19)
List<>
和IEnumerable<>
不是类型;他们是type definitions。因此,问一个是否可分配给另一个并没有什么意义。两者都不能分配给。例如,您不能声明变量List<> a = null
-您将收到编译错误“意外使用未绑定的通用名称。”
当您指定type参数时,类型定义将成为通用类型。此时,它是一种类型,可以分配给它。因此,例如可以将List<string>
分配给IEnumerable<string>
。
如果您牢记类型定义,并且想要进行类型兼容性检查,只需使用<object>
(如果存在类型约束,则使用合适的类型)代替<>
:< / p>
var type1 = typeof(List<object>);
var type2 = typeof(IEnumerable<object>);
//returns true
type2.IsAssignableFrom(type1);
答案 1 :(得分:16)
尽管John Wu对类型定义和实际类型之间的区别写了一个很好的答案,但我认为它不能完全回答OP中提出的问题。
是否有任何方法可以检查两个打开类型之间的关系, 还是仅在封闭类型上存在关系?
首先,关系始终存在;在List<>
类型的定义中可以看到,每个IEnumerable<>
始终都是List<T>
,
public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, ...
但是,如果有一种方法可以检查两种类型之间是否存在这种关系,那么这不能回答您的第二个问题。您可能会认为IsAssignableFrom
方法可用于检查 open 类型之间是否存在关系,但是您不能这样做。为什么?让我们在IsAssignableFrom
function的文档中查找:
Type.IsAssignableFrom(类型c)如果满足以下任一条件,则返回true:
- c和当前实例代表相同的类型。
c直接或间接地从当前派生 实例。如果c直接从当前实例派生 从当前实例继承; c间接源自 当前实例,如果它继承一个或多个继承 从当前实例继承的类。
当前实例是c实现的接口。
- 的约束之一。
c是泛型类型参数,当前实例表示
,如果这些条件都不为真或c为空,则为false。
在您的情况下,上述任何条件都不会导致成立,因为:(1)它们不是同一类型。 (2)它们是开放类型,因此不能彼此派生:它们的泛型参数是未知的(unknown != unknown
)。 List<>
实现封闭的IEnumerable<T>
类型,而不实现开放的IEnumerable<>
类型(3)。它们不是泛型类型参数(4)。
要确定两个泛型类型是否有关系,您需要检查它们的type definitions
(及其嵌套接口/基类型)并验证它们是否有关系:
public static bool IsAssignableToOpenGenericType(Type givenType, Type genericType)
{
var interfaceTypes = givenType.GetInterfaces();
foreach (var it in interfaceTypes)
{
if (it.IsGenericType && it.GetGenericTypeDefinition() == genericType)
return true;
}
if (givenType.IsGenericType && givenType.GetGenericTypeDefinition() == genericType)
return true;
Type baseType = givenType.BaseType;
if (baseType == null) return false;
return IsAssignableToGenericType(baseType, genericType);
}
将导致:
var typ1 = typeof(List<>);
var typ2 = typeof(IEnumerable<>);
// true, List<>'s type definition contains an IEnumerable<>
Console.WriteLine(IsAssignableToOpenGenericType(typ1, typ2));
// false, IEnumerable<>'s type definition does not contain List<>
Console.WriteLine(IsAssignableToOpenGenericType(typ2, typ1));