INF.04 · C# · lekcja 21

Struktury (struct) w C#

Sprawdź, co kopiuje przypisanie struct

  • Zrozum
  • Sprawdź
  • Napisz sam

Po tej lekcji: sprawdź, co kopiuje przypisanie struct

Dzisiaj zrobisz konkretną rzecz

Podstawa programowa (INF.04.3.2) wymaga umiejętności posługiwania się "rekordami" — czyli złożonymi typami danych grupującymi kilka wartości w jedną paczkę. W C# taką rolę pełni struct. To lekki sposób na opisanie "punktu na mapie", "koloru RGB" czy "daty" bez pisania pełnej klasy — a jednocześnie świetna okazja, żeby zrozumieć różnicę między typami wartościowymi a referencyjnymi, która w C# jest fundamentalna.

Typ Point (punkt x, y) w bibliotekach graficznych .NET jest strukturą — tworzysz ich tysiące podczas rysowania (każdy piksel, każdy wierzchołek), więc lekkość struktury i brak potrzeby "odśmiecania" pamięci przez garbage collector realnie przyspiesza program. Podobnie DateTime (data i godzina) oraz decimal (dokładne liczby, np. do kwot pieniężnych) w C# to struktury.

Sprawdź w działaniu

Sprawdź, co kopiuje przypisanie struct

Najpierw przewidź wynik. Zmień dane w modelu i porównaj efekt. Spróbuj także przypadku granicznego, a nie tylko pierwszego poprawnego przykładu.

Ta interakcja wymaga włączonego JavaScriptu.

Model pokazuje wybraną zasadę. Nie uruchamia ani nie ocenia wklejonego kodu C#.

1. Przygotuj i uruchom własny projekt

2. Najpierw mały przykład

Wykonaj tę próbę przed dłuższym rozwiązaniem. Przypisanie struct kopiuje wartość, więc zmiana b.X nie zmienia a.X. Nie oznacza to, że każdy struct zawsze leży na stosie. Pole będące referencją nadal kopiuje referencję, nie cały wskazany obiekt.

Program.cscsharp
Punkt a = new() { X = 1 };
Punkt b = a;
b.X = 9;
Console.WriteLine(a.X);
Console.WriteLine(b.X);

struct Punkt { public int X; }
Wynik
1
9
  1. Co sprawdzić? Zmień jedną wartość i przed uruchomieniem zapisz przewidywany rezultat. Porównaj go z opisem wyniku; przy błędzie wróć do konkretnej instrukcji.

Jak to działa — 1

struct wygląda i zachowuje się bardzo podobnie do klasy — może mieć pola, konstruktory, metody, właściwości. Kluczowa różnica leży głębiej, w pamięci:

• klasa to typ referencyjny — zmienna przechowuje adres obiektu na stercie (heap). Kopiując zmienną, kopiujesz tylko adres — obie zmienne wskazują na ten sam obiekt;

• struct to typ wartościowy — zmienna przechowuje wartość struktury; miejsce przechowywania zależy od kontekstu, nie od samego słowa struct. Kopiując zmienną struktury, dostajesz kopię wartości pól; jeśli pole jest referencją, obie kopie mogą wskazywać ten sam obiekt.

To ma realne konsekwencje: jeśli przekażesz obiekt klasy do metody i metoda zmieni jego pole, zmiana będzie widoczna także poza metodą (bo to wciąż ten sam obiekt). Jeśli przekażesz strukturę, metoda dostaje kopię — zmiany w niej nie wpłyną na oryginał.

Kiedy używać struct, a kiedy class? Struktury sprawdzają się dla małych, niezmiennych (albo rzadko zmienianych) paczek danych bez własnej "tożsamości" — punkt (x, y), kolor (r, g, b), zakres dat. Klasy sprawdzają się tam, gdzie obiekt ma zachowanie, cykl życia, dziedziczenie albo powinien być współdzielony między różnymi częściami programu. W praktyce na start i na egzaminie wystarczy zapamiętać: struct = mała paczka danych kopiowana przez wartość, class = zmienna zawierająca referencję; przypisanie kopiuje referencję.

3. Pełny przykład — czytaj od Main

Teraz zastąp Program.cs tym programem. Zanim uruchomisz, znajdź dane wejściowe, operację i miejsce prezentacji wyniku. W tym wariancie zachowujemy jawny Main. Nie wklejaj kilku pełnych programów do jednego pliku.

Program.cs — osobne uruchomieniecsharp
using System;

// Struktura opisująca punkt na płaszczyźnie.
struct Punkt
{
    public int X;
    public int Y;

    public Punkt(int x, int y)
    {
        X = x;
        Y = y;
    }

    public double OdlegloscOdZera()
    {
        return Math.Sqrt(X * X + Y * Y);
    }

    public override string ToString()
    {
        return $"({X}, {Y})";
    }
}

class Program
{
    // Metoda dostaje KOPIĘ struktury — zmiany wewnątrz nie wyjdą na zewnątrz.
    static void SprobujPrzesunac(Punkt p)
    {
        p.X += 100;
        p.Y += 100;
        Console.WriteLine($"Wewnątrz metody: {p}");
    }

    static void Main()
    {
        Punkt p1 = new Punkt(3, 4);
        Punkt p2 = p1; // kopiowana jest cała struktura, nie referencja

        p2.X = 99;

        Console.WriteLine($"p1 = {p1}");                       // (3, 4) - bez zmian!
        Console.WriteLine($"p2 = {p2}");                       // (99, 4)
        Console.WriteLine($"Odległość p1 od zera: {p1.OdlegloscOdZera():F2}");

        SprobujPrzesunac(p1);
        Console.WriteLine($"p1 po SprobujPrzesunac: {p1}");    // wciąż (3, 4)!
    }
}
  1. Prześledź kod Struktura Punkt ma dwa pola (X, Y), konstruktor ustawiający je od razu przy tworzeniu oraz dwie metody. Kluczowy moment jest w Main: Punkt p2 = p1; tworzy pełną, niezależną kopię — zmiana p2.X nie rusza p1.X. Gdyby Punkt był klasą, ta sama linijka sprawiłaby, że p1 i p2 wskazywałyby na ten sam obiekt, a zmiana jednej zmieniałaby też drugą. Ta sama zasada widać w SprobujPrzesunac — metoda dostaje kopię, więc zmiany w niej "znikają" po jej zakończeniu.

Sprawdź, zanim przepiszesz cały program

Oczekiwanie, że zmiana pola struktury przekazanej do metody "wyjdzie na zewnątrz" — bez słowa kluczowego ref metoda zawsze dostaje kopię struktury. To zaskakuje osoby przyzwyczajone do klas.

Tworzenie dużych, "ciężkich" struktur — jeśli struktura ma wiele pól albo duże dane, każde kopiowanie (a dzieje się to często i niejawnie) kosztuje pamięć i czas. Zasada praktyczna: struct dla małych paczek danych (kilka prostych pól), class dla wszystkiego większego.

Mylenie struct z klasą przy dziedziczeniu — struktury w C# NIE mogą dziedziczyć po innej strukturze ani klasie (mogą tylko implementować interfejsy). Jeśli potrzebujesz dziedziczenia, potrzebujesz klasy.

Ćwiczenie z pomocą · samodzielna praca

4. Teraz Twoja zmiana

Zmień Punkt na klasę (usuń słowo struct, dodaj class) i uruchom ten sam kod z lekcji jeszcze raz. Zaobserwuj, jak zmienia się wynik — p1 teraz też się zmieni po modyfikacji p2. To najlepszy sposób, żeby poczuć różnicę na własne oczy.

Oczekiwany rezultat
Spełnij warunki polecenia. Zapisz wejście, otrzymany wynik i wyjaśnij, która część kodu za niego odpowiada.
Potrzebujesz pomocy? Rozwiązuj krok po kroku.
Krok 1 — pokaż podpowiedź

Wróć do podstawowej formy oraz objaśnienia: Przypisanie struct kopiuje wartość, więc zmiana b.X nie zmienia a.X. Nie oznacza to, że każdy struct zawsze leży na stosie. Pole będące referencją nadal kopiuje referencję, nie cały wskazany obiekt.

Krok 2 — pokaż podpowiedź

Zmień tylko część potrzebną do polecenia. Zachowaj działającą wersję przed zmianą i uruchamiaj po każdym małym etapie.

Krok 3 — pokaż podpowiedź

Sprawdź przypadek zwykły i graniczny; porównaj z regułą: 1 9

Otwórz tylko tyle podpowiedzi, ile potrzebujesz. Po każdym kroku wróć do zadania i sprawdź wynik.

5. Sprawdź rozumienie, nie tylko uruchomienie

Dalej na swoim koncie

Rozgrzewki, quiz i zadania są w biurku ucznia.

Teoria jest otwarta dla wszystkich. Żeby rozwiązywać zadania, zapisywać szkice i widzieć swój postęp, zaloguj się.

Zaloguj się →
Źródła i dalsza nauka