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

21

List<>IEnumerable<> 之间是否有关联?

示例:

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

//return false
type2.IsAssignableFrom(type1);

有没有办法检查两个开放类型之间的关系,或者这种关系只存在于封闭类型中?


1
请注意,每个泛型参数都是唯一的。这意味着 List<T(from List<T>)> 实现了 IEnumerable<T(from List<T>)> 但不实现 IEnumerable<T(from IEnumerable<T>)> - user4003407
4
你实际想要达成什么目标?如果我们知道你试图解决的初始问题,可能会提供其他不依赖于比较开放的通用类型的解决方案。 - Damien_The_Unbeliever
抱歉进行了大幅编辑,但我认为只有前三行表达的问题才是真正有趣的部分。你在其余代码中尝试做什么非常不清楚,正如@Damien_The_Unbeliever所述,你必须进一步解释。 - marsze
@Damien_The_Unbeliever 我试图将这样的表达式进行转换:Expression<Func<IEnumerable<Typea>, Typea, bool>> expression1 = (values, value) => values.Contains(value); 转换为 Expression<Func<IEnumerable<Typeb>, bool>>,其中 TypeATypeB 是独立的。我需要将 expression1 中的 values 常量转换为一个新类型的 IEnumerable<Typeb>,因此我创建了一个 List<Typeb> 实例来表示 IEnumerable<Typeb>,原始表达式集合可以是任何其他泛型接口。我想检查这些值是否可以分配给 List<>。我对开放类型关系很感兴趣。 - YonF
显示剩余2条评论
2个回答

19

List<>IEnumerable<>不是类型,它们是类型定义。因此,询问一个是否可分配给另一个并没有实际意义。它们都不能被赋值。例如,你不能声明一个变量List<> a = null,否则会得到编译错误“意外使用未绑定的泛型名称。”

当你指定类型参数时,类型定义成为一个泛型类型。此时,它是一种类型,可以被分配。例如,List<string>可以分配给IEnumerable<string>

如果您有一个类型定义在脑海中,并且想要进行类型兼容性检查,只需使用<object>(或适当的类型,如果有类型约束)代替<>

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

//returns true
type2.IsAssignableFrom(type1);

谢谢您的回答。这是一种简要的检查方法。我可以理解type2.IsAssignableFrom(type1)的行为。但是,您是否意味着两个开放式泛型类型在成为封闭类型或有界类型之前是独立的或没有关系? - YonF
当你说“related”时,你能具体说明一下你的意思吗?当然它们有某些关系,也有其他的没有。 - John Wu
继承、实现等关系之间存在联系,但我不确定开放泛型类型之间是否存在任何关系。@Jevgeni Geurtsen认为特殊关系列表中存在AssignableFrom。 - YonF
信不信由你,通用类型定义没有任何实现。只有在指定类型参数时才会编译实现,并且仅针对该类型(尽管引用类型的实现可以被重复使用,因为所有指针大小都相同,但这只是一种实现细节)。所以你看,询问List<>是否实现了IEnumerable<>并没有太多意义,因为它本来就没有实现。 - John Wu
@Jevgeni Geurtsen认为特殊关系列表AssignableFrom存在。但是它绝对存在于框架代码中,因此关系存在,但只要它没有具体实现,就是无用的,正如John Wu所指出的那样。 - Jevgeni Geurtsen

16
虽然John Wu写了一个很好的答案,解释了类型定义和实际类型之间的区别,但我认为它并没有完全回答OP提出的问题。首先,这种关系总是存在的;每个List<>始终都是一个IEnumerable<>,正如您可以在List<T>类型的定义中看到的那样。但这并没有回答您的第二个问题,即是否有方法可以检查两个类型之间是否存在这样的关系。您可能会认为IsAssignableFrom方法可以用于检查类型之间的关系是否存在,但你不能。为什么?让我们在IsAssignableFrom函数的文档中找到答案。
Type.IsAssignableFrom (Type c) 如果满足以下任一条件,则返回 true:
1. c 和当前实例表示相同的类型。 2. c 直接或间接派生自当前实例。如果 c 从当前实例继承,则 c 直接派生自当前实例;如果 c 从一个或多个继承自当前实例的类的连续系列中继承,则 c 间接派生自当前实例。 3. 当前实例是 c 实现的接口。 4. c 是泛型类型参数,并且当前实例表示 c 的约束之一。
如果这些条件都不成立,或者 c 为 null,则返回 false。
在您的情况下,上述条件都不会成立,因为:(1)它们不是相同的类型。(2)它们不能互相派生,因为它们是开放类型:它们的泛型类型参数是未知的 (unknown != unknown)。List<> 实现了封闭的 IEnumerable<T> 类型,而不是开放的 IEnumerable<> 类型 (3)。它们不是泛型类型参数 (4)。
要查找两个泛型类型是否有关系,需要检查它们的类型定义(以及它们的嵌套接口/基础类型),并验证它们是否有关系。
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));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接