Typy generyczne (klasy i metody)
Część sekcji Programowanie obiektowe ścieżki C# w Coddy. Lekcja 42 z 70.
Typy generyczne pozwalają pisać klasy i metody działające z dowolnym typem danych przy zachowaniu bezpieczeństwa typów. Zamiast pisać osobne klasy dla IntList, StringList i DoubleList, piszesz jedną klasę generyczną, która obsługuje je wszystkie.
Klasa generyczna używa parametru typu, zazwyczaj nazwanego T, ujętego w nawiasy ostrokątne:
public class Box<T>
{
private T content;
public void Store(T item) => content = item;
public T Retrieve() => content;
}
// Użycie z różnymi typami
Box<int> intBox = new Box<int>();
intBox.Store(42);
Box<string> stringBox = new Box<string>();
stringBox.Store("Hello");Gdy tworzysz Box<int>, kompilator zastępuje każde T przez int. Zapewnia to sprawdzanie typów w czasie kompilacji — nie możesz przypadkowo przechować ciągu znaków w pudełku typu int.
Metody generyczne działają podobnie, umożliwiając pojedynczej metodzie operowanie na różnych typach:
public static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y); // x=2, y=1Kompilator wywnioskuje typ z argumentów, więc nie musisz jawnie wpisywać Swap<int>. Typy generyczne stanowią podstawę kolekcji, takich jak List<T> i Dictionary<TKey, TValue>, umożliwiając tworzenie wielokrotnego użytku, bezpiecznego pod względem typów kodu.
Wyzwanie
ŁatwyZbudujmy generyczny system Storage<T>, który może przechowywać elementy dowolnego typu i nimi zarządzać. Utworzysz wielokrotnego użytku klasę kontenera, która pokazuje możliwości typów generycznych — napisz raz, używaj z liczbami całkowitymi, ciągami znaków lub dowolnym innym typem!
Podzielisz kod na dwa pliki:
Storage.cs: Utwórz generyczną klasęStorage<T>w przestrzeni nazwDataStorage. Kontener powinien:- Mieć prywatną tablicę typu
Tdo przechowywania elementów oraz właściwośćCount, która śledzi liczbę przechowywanych elementów - Przyjmować pojemność w konstruktorze, aby zainicjować wewnętrzną tablicę
- Mieć metodę
Add(T item), która dodaje element, jeśli jest miejsce - Mieć metodę
Get(int index), która zwraca element o podanym indeksie - Mieć metodę
GetAll(), która zwraca wszystkie przechowywane elementy połączone za pomocą", "
- Mieć prywatną tablicę typu
Program.cs: W głównym pliku pokaż działanie swojej klasy generycznej z dwoma różnymi typami. UtwórzStorage<int>iStorage<string>, dodaj elementy do obu i pokaż, że ta sama klasa działa bezproblemowo z obydwoma typami.
Otrzymasz cztery dane wejściowe:
- Pierwsza liczba całkowita do przechowania
- Druga liczba całkowita do przechowania
- Pierwszy ciąg znaków do przechowania
- Drugi ciąg znaków do przechowania
Utwórz magazyn liczb całkowitych o pojemności 5 oraz magazyn ciągów znaków o pojemności 5. Dodaj dwie liczby całkowite do magazynu liczb całkowitych, a dwa ciągi znaków do magazynu ciągów znaków.
Wyświetl dane wyjściowe w tym formacie:
Int storage: {GetAll()}
Int count: {Count}
First int: {Get(0)}
String storage: {GetAll()}
String count: {Count}
First string: {Get(0)}Na przykład, jeśli dane wejściowe to 42, 100, Hello i World, dane wyjściowe powinny wyglądać tak:
Int storage: 42, 100
Int count: 2
First int: 42
String storage: Hello, World
String count: 2
First string: HelloZauważ, że klasę Storage<T> piszesz raz, ale działa ona doskonale zarówno z int, jak i string — na tym polega magia typów generycznych! Kompilator zapewnia bezpieczeństwo typów, więc nie możesz przypadkowo dodać ciągu znaków do magazynu liczb całkowitych.
Spróbuj swoich sił
using System;
using DataStorage;
class Program
{
public static void Main(string[] args)
{
// Wczytaj dane wejściowe
int num1 = Convert.ToInt32(Console.ReadLine());
int num2 = Convert.ToInt32(Console.ReadLine());
string str1 = Console.ReadLine();
string str2 = Console.ReadLine();
// TODO: Utwórz Storage<int> o pojemności 5
// TODO: Dodaj dwie liczby całkowite do magazynu liczb całkowitych
// TODO: Utwórz Storage<string> o pojemności 5
// TODO: Dodaj dwa ciągi znaków do magazynu ciągów znaków
// TODO: Wypisz wynik w wymaganym formacie
// Int storage: {GetAll()}
// Liczba liczb całkowitych: {Count}
// Pierwsza liczba całkowita: {Get(0)}
// String storage: {GetAll()}
// String count: {Count}
// First string: {Get(0)}
}
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrznePrzestrzenie nazw i dyrektywyWprowadzenie do klas i obiektówSłowo kluczowe „this”Metody i parametryPola a właściwościKonstruktoryInicjalizatory obiektówPodsumowanie — prosty kalkulator4Dziedziczenie
Podstawowa składnia dziedziczenia (:)Słowo kluczowe 'base'Słowa kluczowe virtual i overrideKlasy sealedBazowa klasa 'object'Powtórzenie – hierarchia pracowników7Zaawansowane funkcje
Przeciążanie operatorówIndeksery (this[])Nadpisywanie ToString()Metody rozszerzającePodsumowanie – własna lista2Właściwości i składowe statyczne
Właściwości automatyczneWłaściwości tylko do odczytu i zapisuPola i metody statyczneKlasy statyczneSkładowe z ciałem wyrażeniowym5Polimorfizm i interfejsy
Polimorfizm czasu kompilacji a czasu wykonaniaInterfejs a klasa abstrakcyjnaWiele interfejsówJawne implementowanie interfejsówRzutowanie w górę i w dółPowtórka — kalkulator kształtów8Zaawansowane koncepcje OOP
Kompozycja zamiast dziedziczeniaTypy generyczne (klasy i metody)Delegaty i zdarzeniaAtrybuty i refleksjaIDisposable i instrukcja usingPodstawy wstrzykiwania zależności11Wzorce projektowe, część 2
Wzorzec poleceniaWzorzec adapteraWzorzec dekoratoraWzorzec metody szablonowejWzorzec stanuWzorzec kompozytu3Architektura klas
Dane instancji a dane statyczneSłowa kluczowe 'readonly' i 'const'Pola zapasowePowtórzenie — menedżer kont bankowych6Hermetyzacja
Modyfikatory dostępuWłaściwości w hermetyzacjiImplementacja ukrywania danychWzorce niezmiennościPowtórka — dane studentów9Argumenty zmiennej liczby
Słowo kluczowe „params”Parametry opcjonalneArgumenty nazwanePrzeciążanie metodPoćwicz samodzielnie: Kompilator C# online