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

What is the general rule to check if IEnumerable<T1> covariant to IEnumerable<T2> ?

I did some experiments:


one.

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

This works because IEnumerable<out T> is covariant, and String inherits from 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 is actually an Object , but it does not work, because Object is a reference type and MyStruct is a value type. Ok, I see some logic here.

3.

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

Should work, because IEnumerable<out T> is covariant and MyStruct IS ValueType , but does not work ... OK, maybe MyStruct does not actually inherit ValueType ....

4.

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

Even so: "Unable to convert MyStruct to MyInterface." Oh really?? You just did this one line earlier ...


I tried to formulate a general rule:

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

So the questions are, how to determine if IEnumerable<T1> covariant to IEnumerable<T2> ? Is my IsCovariantIEnumerable(...) function correct? If so, is there an easier way to verify this? If not, how to fix it?

See also these articles: 1 , 2 .

+6
source share
2 answers

In your particular case, this does not work, because value types do not support covariance.

But to the question, how to determine if IEnumerable<T2> co-option to IEnumerable<T1> :

The Type.IsAssignableFrom() method reports whether Type.IsAssignableFrom() can be an instance of a specific type of a variable of this type. So you can implement your method like this:

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

Using:

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

But IsCovariantIEnumerable(typeof(object), typeof(MyStruct)) will return false for the reason mentioned above.


For completeness: Of course, you do not need an additional method, since you can easily make typeof(IEnumerable<object>).IsAssignableFrom(typeof(IEnumerable<string>) .

+5
source

Value types do not support covariance, as it will change their internal representation [1] .

If you want to avoid strange cases, I would recommend using IsAssignableFrom :

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

+2
source

All Articles