Menu
Coddy logo textTech

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=1

Kompilator 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.

challenge icon

Wyzwanie

Łatwy

Zbudujmy 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 nazw DataStorage. Kontener powinien:
    • Mieć prywatną tablicę typu T do 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ą ", "
  • Program.cs: W głównym pliku pokaż działanie swojej klasy generycznej z dwoma różnymi typami. Utwórz Storage<int> i Storage<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: Hello

Zauważ, ż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)}
    }
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C# online