如何检查 `IEnumerable<T1>` 是否与 `IEnumerable<T2>` 协变?

How to check if `IEnumerable<T1>` covariant to `IEnumerable<T2>`?

检查 IEnumerable<T1> 是否与 IEnumerable<T2> 协变的通用规则是什么?

我做了一些实验:


1.

Object Obj = "Test string";
IEnumerable<Object> Objs = new String[100];

有效,因为 IEnumerable<out T> 是协变的并且 String 继承了 Object

2.

interface MyInterface{}
struct MyStruct:MyInterface{}
.....
Object V = new MyStruct();
Console.WriteLine(new MyStruct() is Object); // Output: True. 
IEnumerable<Object> Vs = new MyStruct[100]; // Compilation error here

MyStruct实际上是一个Object,但它不起作用,因为Object是引用类型而MyStruct是值类型。好的,我在这里看到了一些逻辑。

3.

Console.WriteLine(new MyStruct() is ValueType); // Output: "True"
ValueType V2 = new MyStruct();
IEnumerable<ValueType> Vs2 = new MyStruct[100]; // Compilation error here

应该可以工作,因为 IEnumerable<out T> 是协变的,而 MyStructValueType,但不工作......好吧,也许 MyStruct 不实际上继承了 ValueType.....

4.

MyInterface V3 = new MyStruct(); 
Console.WriteLine(V3 is MyInterface); // Output: "True" 
IEnumerable<MyInterface> Vs3 = new MyStruct[100]; // Compilation error here

即使这样:"Cannot convert MyStruct to MyInterface"。哦真的吗??你刚刚在一行之前做了...


我尝试制定通用规则:

public static bool IsCovariantIEnumerable(Type T1, Type T2  ){          
    return (T2.IsAssignableFrom(T1)) && !T2.IsValueType; // Is this correct??
}

那么,问题是如何实际确定 IEnumerable<T1> 是否与 IEnumerable<T2> 协变?我的 IsCovariantIEnumerable(...) 函数正确吗?如果是,有没有更简单的方法来检查它?如果没有,如何解决?

另请参阅这些文章:1, 2

值类型不支持协变,因为它会改变它们的内部表示 [1]

如果您想避免奇怪的情况,我建议您改用 IsAssignableFrom

public static bool IsCovariantIEnumerable(Type T1, Type T2) => T1.IsAssignableFrom(T2);

在您的特定情况下它不起作用,因为值类型不支持协方差。

但是对于如何确定 if 的问题,IEnumerable<T2>IEnumerable<T1> 是协变的:

方法 Type.IsAssignableFrom() 告诉您是否可以将某个类型的实例分配给该类型的变量。所以你可以像这样实现你的方法:

public static bool IsCovariantIEnumerable(Type T1, Type T2)
{
    Type enumerable1 = typeof(IEnumerable<>).MakeGenericType(T1);
    Type enumerable2 = typeof(IEnumerable<>).MakeGenericType(T2);
    return enumerable1.IsAssignableFrom(enumerable2);
}

用法:

if (IsCovariantIEnumerable(typeof(object), typeof(string))
    Console.WriteLine("IEnumerable<string> can be assigned to IEnumerable<object>");

但是 IsCovariantIEnumerable(typeof(object), typeof(MyStruct)) 会 return false 出于上述原因。


为了完整性:当然你不需要额外的方法,因为你可以很容易地做到typeof(IEnumerable<object>).IsAssignableFrom(typeof(IEnumerable<string>)