2016-08-30 44 views
6

Jaka jest wspólna reguła, aby sprawdzić, czy IEnumerable<T1> kowariant do ?Jak sprawdzić, czy `IEnumerable <T1>` covariant do `IEnumerable <T2>`?

Zrobiłem kilka eksperymentów:


1.

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

działa, ponieważ IEnumerable<out T> jest kowariantna i String dziedziczy 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 jest rzeczywiście Object, ale to nie działa, ponieważ Object jest typ referencyjny i MyStruct jest typ wartości. OK, widzę tutaj pewną logikę.

3.

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

Powinna działać, ponieważ IEnumerable<out T> jest kowariantna i MyStruct JEST ValueType, ale nie działa ... OK, może MyStruct rzeczywistości nie inheritst ValueType ....

4.

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

Nawet w ten sposób: "Nie można przekonwertować MyStruct na MyInterface". Naprawdę?? Po prostu to zrobił jeden wiersz przed ...


Próbowałem sformułować wspólną zasadę:

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

Więc pytanie brzmi, jak ustalić, czy rzeczywiście IEnumerable<T1> kowariantna do IEnumerable<T2> ? Czy moja funkcja IsCovariantIEnumerable(...) jest poprawna? Jeśli tak, czy istnieje jakiś prostszy sposób na sprawdzenie? Jeśli nie, jak to naprawić?

Zobacz także te artykuły: 1, 2.

+0

Typy wartości po prostu nie obsługują współzmienności; nie powinno to dziwić, ponieważ typy wartości nie wspierają wcale * dziedziczenia *. Jeśli potrzebujesz użyć typu wartości jako interfejsu i/lub 'obiektu', musisz go zaznaczyć - np. 'array.Cast '. Nie jest jasne, jakiej części tego nie rozumiesz, biorąc pod uwagę, że pytania, które łączysz, odpowiadają również na to pytanie :) – Luaan

+0

@Luaan, chcę tylko mieć pełną regułę, aby sprawdzić, czy 'IEnumerable ' kowariancja do 'IEnumerable '. Nadal nie jestem pewien, czy moja funkcja 'IsCovariantIEnumerable()' jest poprawna. – Astronavigator

+1

Pod względem obsługi interfejsów, ['IsAssignableFrom'] (https://msdn.microsoft.com/en-us/library/system.type.isassignablefrom (v = vs.110) .aspx) może działać lepiej niż' IsSubclassOf ' – grek40

Odpowiedz

5

W Twoim konkretnym przypadku to nie działa, ponieważ typy wartości nie wspierają współpracę wariancji.

Ale na pytanie, jak ustalić jeśliIEnumerable<T2> jest współfinansowany wariant do IEnumerable<T1>:

Sposób Type.IsAssignableFrom() informuje, czy wystąpienie pewnego rodzaju jest assignalbe do zmiennej tego typu. Więc można wdrożyć metodę tak:

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

Zastosowanie:

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

Ale IsCovariantIEnumerable(typeof(object), typeof(MyStruct)) będzie return false z powodu opisanego powyżej.


Dla kompletności: oczywiście, że nie potrzebują dodatkowej metody, jak można łatwo zrobić typeof(IEnumerable<object>).IsAssignableFrom(typeof(IEnumerable<string>).

+0

Faktycznie sprawdzenie typu 'IEnumerable <...>' dla możliwości przypisania jest zdecydowanie prostsze niż wcześniejsze zgadywanie zachowania typu elementu. Czyste rozwiązanie, nie myślałem. – grek40

+0

@ grek40 Masz rację, myślałem o implementacji metody OP i tym samym zapomniałem bezpośredniego połączenia z 'IsAssignableFrom'. Dodano to dla kompletności. –

+0

Widzę, że twoje 'IsCovariantIEnumerable' działa dobrze. Ale wciąż pytanie brzmi: "Czy mój" IsCovariantIEnumerable "również jest poprawny czy nie?" Czy są jakieś przykłady, gdzie twoja funkcja działa, ale moja nie działa? – Astronavigator

2

Typy wartości nie obsługują kowariancji, ponieważ zmieniłoby to ich wewnętrzną reprezentację [1].

Jeśli chcesz uniknąć dziwnych przypadków polecam korzystania IsAssignableFrom zamiast:

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