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.
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.
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; }
}
Diuna Hobbit
- 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.
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();
}
}
- 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, żekopiaifilm1to DWA OSOBNE obiekty w pamięci — zmianakopia.Cenaw żaden sposób nie zmieniafilm1.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.
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ę →