Jason. Cała informatyka w jednym miejscu!

Poświęcę dzisiejszy materiał na wyjaśnienie podstaw programowania generycznego w "CSharpie". Temat już bardziej zaawansowany i wymagający intensywniej pracujących szarych komórek. Dowiedzcie się chociaż jak wygląda taki typ generyczny w języku C#. Dawajcie, to także jest ważne!

TYP GENERYCZNY W JĘZYKU C# PRZYPOMINA "KODOWEGO KAMELEONA"

Programowanie generyczne, programowanie ogólne, typy ogólne, "genericsy". Usłyszałem już wiele terminów, które dotyczą tego samego, a robię w tym fachu dopiero niewiele ponad dziesięć lat (licząc te lata nieoficjalne). Chodzi o wstawienie w miejsce klasy albo funkcji (takie dwa najczęściej spotykane miejsca) typu, który podczas tworzenia instancji albo wywoływania funkcji może przeistoczyć się w dowolny typ skonkretyzowany. Innymi słowy, programujemy klasę albo funkcję na kształt uniwersalny, a podczas wykorzystywania ustalamy typ jaki ma się tam znaleźć. I co najlepsze, to może być za każdym razem inny typ :O! Za pierwszym razem może to być typ danych "int", za drugim "string", a za trzecim jakaś klasa. Dlatego typ generyczny w języku C# przypomina takiego "kameleona", który za każdym razem może stać się dowolnym typem.

TROCHĘ HISTORII NIE ZASZKODZI

Od wersji C# 2.0, typy generyczne stoją dla programistów otworem. Są one odpowiedzią na podatny na błędy kod, który miał za zadanie przechowywać dane różnych typów. Każdorazowe sprawdzanie rzeczywistego typu, weryfikacja poprawności, wykładanie się na łopatki programu na etapie jego uruchomienia i tym podobne sprawy sprawiłyby, że większość programistów by się powiesiła od tego wszystkiego :D. Dlatego twórcy zdecydowali się dodać do języka typy generyczne, które mają za zadanie nie tylko umożliwić łatwe wszczepianie do klas i funkcji typów ogólnych, ale także przerzucić odpowiedzialność sprawdzania poprawności na kompilator, tak aby wykrywać błędy na etapie kompilacji. Nie stanowi to żadnej ekskluzywnej cechy języka. Stało się to już fundamentem w wielu językach programowania wysokiego poziomu. Java też czymś takim dysponuje od wersji 5.0 (2004).

NIE ZROZUM TEGO ŹLE!

Czy to działa tak, jakbyśmy wstawili "matkę wszystkich klas", "Object"? Nie, to nie to samo. Mimo faktu, iż to klasa najbardziej ogólna ze wszystkich, to ona wciąż jest typem skonkretyzowanym. Chodzi mi o taki typ "kameleon", który podczas wykorzystywania w kodzie na zewnątrz może stać się dowolnym typem już konkretnym, gdyż przy definicji musimy już się opowiedzieć za tym, co KONKRETNIE ma być reprezentowane przez ten typ generyczny. Nie stawiajcie znaku równości pomiędzy klasą "Object", a typem generycznym!

LITERA 'T' STAŁA SIĘ ZNAKIEM ROZPOZNAWCZYM

Poznajcie się teraz z zapisem w kodzie źródłowym. Typ generyczny w języku C# także posiada własną konwencję nazewniczą zgodnie z którą najlepiej jest oznaczać wielką literą 'T' jak "tramwaj". Macie prawo do wstawienia kilku takich typów. Wówczas nazwa każdego z nich powinna być każdą następną literą w alfabecie, czyli 'U', 'V' itd., ale nie jest to żaden rygor ;).

Tak może wyglądać przykładowa klasa wykorzystująca typ ogólny:

class MyGenericClass<T>
{
	
}

A jeśli potrzeba więcej typów:

class MyGenericClass<T, U, V>
{
	
}

to wymieniamy je po przecinku. Kiedy "generic" ma się znaleźć w klasie, wstawiamy jego etykietę w nawiasy kątowe, zaraz po nazwie klasy. A jeśli chodzi o instancję:

MyGenericClass<string> mgc = new MyGenericClass<string>();

Tak jest. Dokładnie tak.

PRZYKŁAD Z WŁAŚCIWOŚCIĄ W TLE

Pokażę Wam jeszcze jaki to ma wpływ na składowe wykorzystujące typ generyczny w języku C#. Aby jeszcze bardziej Wam uzmysłowić to "przebieranie się" w konkretny typ, dajmy na to że nasza klasa z typem generycznym zawiera w sobie właściwość:

class MyGenericClass<T>
{
	public T Property {get; set;}
}

Da to taki efekt, że po opowiedzeniu się za tym, jaki typ konkretny ma brać udział, to ta właściwość TAKŻE przyjmie ten sam typ. Dla tej instancji:

MyGenericClass<string> mgc = new MyGenericClass<string>();

mgc.Property = "Przebrałem się w łańcuch znaków!";

będzie to przechowywać łańcuch znaków. Z kolei dla poniższej:

MyGenericClass<float> mgc = new MyGenericClass<float>();

mgc.Property = 13.55f;

będzie to liczba zmiennoprzecinkowa o pojedynczej precyzji. Teraz powinno być lepiej zrozumiałe jak to wszystko wygląda i jak działa.

JA TEN ZNACZEK JUŻ GDZIEŚ WIDZIAŁEM

Nie wydaje Wam się ;). Istotnie, mogliście już ujrzeć podobny zapis w trakcie wykorzystywania kolekcji, które są pokryte typami generycznymi do szpiku. Tego typu zapisy:

List<string> list = new List<string>();
HashSet<int> hashSet = new HashSet<int>();
Dictionary<int, Student> dictionary = new Dictionary<int, Student>();

są niczym innym jak właśnie symbolem zdefiniowania typu KONKRETNEGO w miejsce typu generycznego :D.

Klasa wykorzystująca typ generyczny w języku C#

Typ generyczny w języku C# umożliwia "wcielanie się" typu w typ konkretny podczas korzystania z klasy (instancja) lub funkcji (wywołanie).


O samych typach generycznych można napisać całą litanię, a i tak okaże się dla wielu osób na pierwszy strzał niezrozumiała (z szacunkiem, przecież ja też tego od samego początku nie kapowałem). To było tylko preludium do tego krętego rozdziału, a w przyszłości napiszę o nim dużo więcej :).

PODOBNE ARTYKUŁY