INF.04 · C# · lekcja 12

Konstruktory (zwykły i kopiujący) i destruktor

Alias i kopia to dwie różne sytuacje

  • Zrozum
  • Sprawdź
  • Napisz sam

Po tej lekcji: alias i kopia to dwie różne sytuacje

Dzisiaj zrobisz konkretną rzecz

Bez konstruktora tworzenie w pełni wypełnionego obiektu wymaga kilku osobnych linii kodu za każdym razem, co jest niewygodne i łatwo o pomyłkę (np. zapomnienie ustawienia jednego pola). Konstruktor gwarantuje, że obiekt od razu "rodzi się" w poprawnym, kompletnym stanie.

Zamawiając samochód u dealera, od razu podajesz wszystkie parametry (kolor, silnik, wyposażenie) — samochód nie "powstaje" najpierw pusty, żeby dopiero potem ktoś ręcznie dokładał mu kolor i silnik. To dokładnie rola konstruktora.

Sprawdź w działaniu

Alias i kopia to dwie różne sytuacje

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. Konstruktor ma nazwę klasy i nie ma typu wyniku. alias = a kopiuje referencję. new Film(a) wywołuje napisany przez nas konstruktor kopiujący. Przy polach referencyjnych trzeba osobno zdecydować o kopii płytkiej lub głębokiej.

Program.cscsharp
var a = new Film("Diuna");
var alias = a;
var kopia = new Film(a);
kopia.Tytul = "Hobbit";
Console.WriteLine(alias.Tytul);
Console.WriteLine(kopia.Tytul);

class Film
{
    public string Tytul { get; set; }
    public Film(string tytul) { Tytul = tytul; }
    public Film(Film inny) { Tytul = inny.Tytul; }
}
Wynik
Diuna
Hobbit
  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

W Lekcjach 9-10 tworzyłeś obiekt przez new Film(), a potem RĘCZNIE przypisywałeś wartości polom w kolejnych liniach. Konstruktor pozwala zrobić to od razu, w jednym kroku, w momencie tworzenia obiektu.

Konstruktor to specjalna metoda o TAKIEJ SAMEJ nazwie jak klasa, bez typu zwracanego, wywoływana automatycznie przy każdym new: public Film(string tytul, int rok, double cena) { Tytul = tytul; Rok = rok; Cena = cena; } Użycie: Film film1 = new Film("Diuna", 2021, 12.99); — obiekt jest od razu w pełni wypełniony.

Konstruktory można PRZECIĄŻAĆ (dokładnie jak zwykłe metody, Lekcja 7) — klasa może mieć kilka konstruktorów o różnej liczbie parametrów, np. jeden przyjmujący wszystkie dane, drugi ustawiający tylko część, a resztę na wartości domyślne.

Konstruktor kopiujący tworzy nowy obiekt jako KOPIĘ istniejącego, przyjmując jako parametr obiekt tej samej klasy i przepisując z niego wartości pól: public Film(Film inny) { Tytul = inny.Tytul; Rok = inny.Rok; Cena = inny.Cena; } Użycie: Film kopia = new Film(film1); — kopia ma te same wartości co film1, ale jest osobną instancją. Dla pokazanych pól wartościowych i niemutowalnych napisów zmiana przypisania pola kopii nie zmienia oryginału. Pole będące np. List<T> wymaga decyzji, czy kopiujemy też wskazywaną listę.

Jak to działa — 2

Destruktor to metoda wywoływana, gdy obiekt jest usuwany z pamięci przez tzw. Garbage Collector (.NET sam automatycznie zarządza pamięcią, w przeciwieństwie do C++) — w C# destruktory są rzadko potrzebne w praktyce, ale warto wiedzieć, że istnieją, do zwalniania rzadkich, specjalnych zasobów: ~Film() { ... }

Finalizator nie jest zwykłym sprzątaniem po metodzie. Nie wiadomo, kiedy zostanie uruchomiony; .NET5+ nie wywołuje wszystkich finalizatorów przy zamykaniu procesu. Dla zasobów stosuj IDisposable i using. Nie dodawaj pustego finalizatora i nie używaj GC.Collect jako codziennej techniki.

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. Wymuszenie GC w tym starszym przykładzie służy wyłącznie obserwacji mechanizmu, nie jest zalecanym sposobem zwalniania zasobów. Nie uzależniaj poprawności programu od kolejności komunikatów finalizatora.

Program.cs — osobne uruchomieniecsharp
using System;

class Film
{
    public string Tytul;
    public int Rok;
    public double Cena;

    // konstruktor - wypełnia obiekt od razu przy tworzeniu
    public Film(string tytul, int rok, double cena)
    {
        Tytul = tytul;
        Rok = rok;
        Cena = cena;
    }

    // konstruktor kopiujący - tworzy NIEZALEŻNĄ kopię innego filmu
    public Film(Film inny)
    {
        Tytul = inny.Tytul;
        Rok = inny.Rok;
        Cena = inny.Cena;
    }

    public void WyswietlInfo()
    {
        Console.WriteLine(Tytul + " (" + Rok + ") - " + Cena + " zł");
    }
}

class Program
{
    static void Main()
    {
        Film film1 = new Film("Diuna: Część Trzecia", 2026, 12.99);
        film1.WyswietlInfo();

        Film kopia = new Film(film1); // konstruktor kopiujący
        kopia.Cena = 9.99; // zmiana kopii NIE wpływa na film1

        Console.WriteLine("Oryginał: ");
        film1.WyswietlInfo();
        Console.WriteLine("Kopia (z inną ceną): ");
        kopia.WyswietlInfo();
    }
}
  1. Prześledź kod Zwróć uwagę, że oba konstruktory mają TĘ SAMĄ nazwę (Film) — C# rozróżnia je po parametrach, dokładnie jak przy przeciążaniu zwykłych metod (Lekcja 7). Kluczowe w konstruktorze kopiującym jest to, że kopia i film1 to DWA OSOBNE obiekty w pamięci — zmiana kopia.Cena w żaden sposób nie zmienia film1.Cena, mimo że na starcie miały identyczne wartości.

Sprawdź, zanim przepiszesz cały program

Częsty błąd to nadanie konstruktorowi innej nazwy niż nazwa klasy — C# rozpozna to wtedy jako zwykłą metodę, a nie konstruktor, i nie będzie wywoływana automatycznie przy new. Drugi błąd: dodanie konstruktorowi typu zwracanego (np. void) — konstruktory NIGDY nie mają typu zwracanego, nawet void. Trzeci: przekonanie, że przypisanie Film kopia = film1; (bez new i konstruktora kopiującego) tworzy niezależną kopię — w rzeczywistości kopia wskazywałaby na TEN SAM obiekt co film1, więc zmiana jednego zmieniłaby też drugi.

Ćwiczenie z pomocą · samodzielna praca

4. Teraz Twoja zmiana

Dodaj do klasy Osoba (z Lekcji 10) konstruktor przyjmujący imię i wiek, i użyj go do utworzenia trzech obiektów w jednej linii każdy.

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: Konstruktor ma nazwę klasy i nie ma typu wyniku. alias = a kopiuje referencję. new Film(a) wywołuje napisany przez nas konstruktor kopiujący. Przy polach referencyjnych trzeba osobno zdecydować o kopii płytkiej lub głębokiej.

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łą: Diuna Hobbit

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