w ,

Typy generyczne na platformie .NET

Typy generyczne, zwane również ogólnymi, weszły wraz z .NET Framework 2.0. Szybko zastały na popularności i przypadły do gustu programistom .NET.Typy generyczne wykorzystują przestrzeń nazw System.Collections.Generic. Zalecane jest, aby to właśnie tych kolekcji wykorzystywać. Jednak zdecydowano, że wcześniejsze kolekcje np. ArrayList zajmujące miejsce w System.Collections pozostaną w głównej mierze dla zachowania kompatybilności.

Jednak co różni kolekcję generyczną od kolekcji niegenerycznych? Przede wszystkim mówimy tutaj o kwestiach wydajnościowych. Elementami ArrayListy były obiekty. Tak więc w chwili dodawania elementów do ArrayListy lista musiała tzw. boxsować (pakować), a następnie w chwili wywołania dany typ unboxsować (wypakować). Każdorazowy zabieg zużywa zasoby sprzętowe.
Drugą mocną cechą generyków jest silne typowanie. Do kolekcji niegenerycznych mogliśmy załadować wszystko, co było częściowo zaletą, ale też i wadą. W chwili odczytywania zawartości tablicy obiektów mogły powstawać błędy. Programista nie ma ścisłej kontroli co zostanie umieszczone w tej kolekcji.
Lista generyczna – List<> jest bardzo elastyczna, przy czym silnie typowa.

Aby zrobić lekkie wprowadzenie, zacznę od tworzenia metod generycznych. Podaję przykład:

public void MyInvert(ref T variable1, ref T variable2)
{
T helper = variable1;
variable1 = variable2;
variable2 = helper;
}

Powyższa funkcja ma za zadanie zamienić wartościami dwa podane argumenty. Metoda MyInvert jest metodą generyczną. To co ją wizualnie wyróżnia to sufiks po jej nazwie. Dowolny znak (niekoniecznie T ale tak się przyjęło wśród programistów) w nawiasach kwadratowych oznacza, że dana metoda jest metodą generyczną. Parametr T może przyjąć dowolny typ.

Wywołując metodę

this.MyInvert(ref one, ref two);

Możemy podać dowolny typ zmiennych. Metoda generyczna jest bardzo elastyczna i sama się dostosuje w zależności od typu otrzymanych argumentów. Oczywiście nie muszą być to stringi. Mogą być to inty, boole itd…
Tak jak wcześniej wspomniałem, ogromną zaletą jest to, że metoda ta sama dostosuje się do naszych potrzeb bez konieczności tworzenia metod, dla każdego z typów osobno.

Istnieje również pojęcie klasy generycznej. Klasa generyczna ma następująca konstrukcję:

class GenericClass
{
T t;
public GenericClass()
{
Console.WriteLine("My first generic class " + t.GetType());
}
}

Nie różni się ona niczym specjalnym od zwyczajnej klasy, poza sufiksem po nazwie klasy. Tak jak w przypadku metod, tak samo w klasach, wpisując <T> po nazwie klasy, oznaczamy, że jest ona generyczna. Powołując do życia obiekt takiej klasy musimy również zadeklarować typ, na jakim chcemy operować w tej klasie, np:

GenericClass gc = new GenericClass();

Powyższa deklaracja znaczy tyle, że wszystkie metody generyczne i zmienne oznakowane typem T przyjmą typ Int32. Wydruk z konsoli dla powyższego przykładu:

My first generic class System.Int32

Możemy również zawęzić podawany typ generyczny poprzez narzucenie na niego interfejsu lub klasy. Wtedy możliwe będzie podawanie tylko typu spełniającego warunek dziedziczenia, czy interfejsu. Przykład:

class GenericClass where T : IEnumerable
{
T t;
public GenericClass()
{
Console.WriteLine("My first generic class " + t.GetType());
}
}

Teraz, przy powoływaniu do życia obiektu tej klasy, musimy podać typ spełniający interfejs IEnumerable. Podanie innej wartości np. int wygeneruje błąd kompilacji.

Napisane przez Przemysław Bykowski

Aktywny programista i energiczny trener. Specjalizuje się w Spring Boot i uczę go w ramach AkademiaSpring.pl. Po godzinach udzielam się na YouTubach. Więcej o mnie.

Dodaj komentarz

Wzorzec projektowy obserwator

Observer, wzorzec projektowy – Twoje obiekty zawsze na bieżąco

Adnotacje w języku Java