List <>和IEnumerable <>开放类型之间的关系

时间:2018-11-20 07:04:20

标签: c# .net

打开类型List<>IEnumerable<>之间是否存在任何关系?

示例:

var type1 = typeof(List<>);
var type2 = typeof(IEnumerable<>);

//return false
type2.IsAssignableFrom(type1);

是否有任何方法可以检查两个打开类型之间的关系,或者该关系仅在闭合类型上存在?

2 个答案:

答案 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:

     
      
  1. c和当前实例代表相同的类型。
  2.   
  3. c直接或间接地从当前派生       实例。如果c直接从当前实例派生       从当前实例继承; c间接源自       当前实例,如果它继承一个或多个继承       从当前实例继承的类。

  4.   
  5. 当前实例是c实现的接口。

  6.   
  7. c是泛型类型参数,当前实例表示       

  8. 的约束之一。

      
     

,如果这些条件都不为真或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);
}

(source)

将导致:

    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));