Intro
C# 8.0 został niedawno opublikowany, jednak w dzisiejszym poście chcę napisać o funkcjonalności z C# 2.0. Wtedy to właśnie firma Microsoft wprowadziła typy generyczne. Każdy wie, każdy używa, ale cz próbowałeś kiedykolwiek napisać coś takiego:
class A<T> where T : A<T> { }
Ok, stop… Masz teraz 2 opcje
- Kontynuuj, ale nie ma zagadki
- Zamknij kartę, porozmyślaj i wróć tutaj
Ten post należy do serii Podejrzane typy.
Spróbuj dotknąć
Odpowiedzmy na pytanie:
Jak mogę stworzyć instancję obiektu A?
Hmm… Spróbujmy!
var a = new A<A<A<...>>>();
To nie wygląda jak droga do sukcesu.
Wprowadźmy zmienną pomocniczą B:
B = A<A<A<...>>>
To oznacza, że:
A<B> = A<A<A<...>>>
Czyli:
B = A<A<A<...>>> = A<B>
Ostatecznie:
B = A<B>
Wspaniale, ale co to oznacza? Nie mogliśmy stworzyć obiektu typu A bezpośrednio, ale czy w powietrzu nie unosi się właśnie woń dziedziczenia?
class B : A<B> { }
Wow! Kompiluje się! Nawet możemy stworzyć obiekt B za pomocą operatora new:
var b = new B();
Przyjrzyj się z bliska
Co się właśnie stało? Co stworzyliśmy? Musisz przyznać, że definicja klasy A jest dziwna interesująca.
Najwyższy czas na drugą zagadkę:
Zakładając poniższą definicję
class B : A<X> { }, czy typ X zawsze musi być równy B?
Niestety odpowiedź brzmi… NIE. Poniżej 2 definicje typu X, który powodują, że powyższy kod się kompiluje:
class X : B { }
class X : A<X> { }
Jeśli znasz inne definicje typu X, to śmiało podziel się nią w komentarzu!
A dlaczego ze smutkiem napisałem, że nie zawsze X musi być tożsamy B? Ano dlatego, że w przeciwnym przypadku mielibyśmy na tacy w .NET Curiously Recurring Template Pattern (CRTP).
Wzorzec ten byłby przydatny w implementacji FluentApi oraz narzucaniu implementowania Api o specyficznych typach. Więcej ciekawych przykładów można znaleźć poście Curiously Recurring Template Pattern (CRTP) in C#.
Konstrukcja z początku tego posta spotkała się z krytyką ze strony Erica Lipperta, z którą można zapoznać się w jego poście Curiouser and curiouser. Główne zarzuty to fakt, że nie jest to poprawna implementacja CRTP (co wykazaliśmy na dwa sposoby również i w tym poście), oraz został zauważony fakt, że taki kod jest trudny w analizie przy pierwszym spotkaniu (czego pewnie doświadczyłaś/eś w pierwszym akapicie).
Niemniej jako zagadka i rozrywka umysłowa sprawdza się znakomicie 🙂