niedziela, 3 marca 2013

Dziedziczenie w c#

Co jakiś czas napotykam na problemy związane z Co- i Contra- wariancją, więc postanowiłem troszkę pobawić się z .NETem. Chciałbym przedstawić dlaczego jest z tym taki problem. Niech poniższy wpis będzie wstępem do większego zagadnienia.
Stwórzmy 2 klasy. Jedna (B) będzie dziedziczyła po drugiej (A)
class A
{
}
 
class B : A
{
}

Aby poprawić czytelność stwórzmy extension method, która będzie określała czy jeden typ dziedziczy od drugiego typu:
 public static class TypeExt
{
 public static bool IsFromType<T>(this Type type)
 {
     return typeof(T).IsAssignableFrom(type);
 }
}

Teraz możemy sprawdzić co zwróci nam ta metoda.
     Console.WriteLine(typeof(A).IsFromType<A>()); //true 
     Console.WriteLine(typeof(B).IsFromType<A>()); //true 
     Console.WriteLine(typeof(A).IsFromType<B>()); //false 

Sprawdzamy jak będzie dla typów generycznych.
 Console.WriteLine(typeof(List<B>).IsFromType<List<B>>()); //true
 Console.WriteLine(typeof(List<B>).IsFromType<IEnumerable<B>>()); //true

Typy generyczne są tak samo traktowane jak 'normalne' typy. Ale co będzie, kiedy argumenty w typach generycznych będą różne lub jeden argument będzie dziedziczony po drugim??
    Console.WriteLine(typeof(List<B>).IsFromType<List<A>>()); //false

Nawet jeżeli typ generyczny będzie taki sam, argumenty w typie generycznym będą inne, ale w relacji dziedziczenia to nie ma zależności. Wtedy musimy porównywać argumenty tych typów generycznych.
public static bool IsGenericArgFromType<T>(this Type type)
{
   var args1 = typeof(T).GetGenericArguments();
   var args2 = type.GetGenericArguments();

   return args1.Length == args2.Length &&
      args1.Zip(args2, (arg1, arg2) => new { arg1, arg2 })
      .All(x => x.arg1.IsAssignableFrom(x.arg2));
}

Console.WriteLine(typeof(List<B>).IsGenericArgFromType<List<A>>()); //true
Console.WriteLine(typeof(List<string>).IsGenericArgFromType<List<object>>()); //true
Pomimo tego, że argumenty w typie generycznym są w relacji dziedziczenia (przyporządkowania) to te dwa type nie są przyporządkowane do siebie. Taki sam problem jest, gdy mamy listę stringów i listę obiektów. W naturalny sposób lista stringów powinna być przyporządkowana do listy obiektów, ale tak nie jest.

Brak komentarzy:

Prześlij komentarz