Pola — pamięć konkretnego obiektu
Robot pamięta poziom energii pomiędzy wywołaniami metod, a zmienna licząca koszt jednego ruchu znika zaraz po użyciu. To dwie różne role i dwa różne miejsca w kodzie.
Czego się dziś nauczysz
- Odróżnisz pole od parametru i zmiennej lokalnej.
- Wskażesz, jak długo żyje każda z nich.
- Podasz wartość domyślną pola dla typu liczbowego, logicznego i tekstowego.
- Ustawisz wartość początkową pola i zabezpieczysz ją słowem
readonly. - Wyjaśnisz, dlaczego dwa obiekty tej samej klasy mają niezależne pola.
Przygotowanie: lekcje 01–37. Przewidywany czas: 45–90 minut z zadaniami. Przykłady wymagają .NET 8 lub nowszego, z włączonymi ImplicitUsings i Nullable.
Trzy rodzaje zmiennych w klasie
W metodzie obiektu spotykają się trzy rodzaje zmiennych. Wyglądają podobnie, a różnią się tym, jak długo żyją.
class Robot
{
private int energia = 10; // POLE
public void Rusz(int odleglosc) // PARAMETR
{
int koszt = odleglosc * 2; // ZMIENNA LOKALNA
energia -= koszt;
Console.WriteLine($"Zuzyto {koszt}, zostalo {energia}");
}
}
| Pole | Parametr | Zmienna lokalna | |
|---|---|---|---|
| Gdzie deklarowane | w klasie, poza metodami | w nawiasie metody | w ciele metody |
| Skąd bierze wartość | z inicjalizatora albo z metod | od wywołującego | z przypisania w metodzie |
| Jak długo istnieje | tak długo jak obiekt | jedno wywołanie metody | jedno wywołanie metody |
| Kto ją widzi | wszystkie metody obiektu | tylko ta metoda | tylko ta metoda |
| Po co jest | pamięć obiektu | dane wejściowe | chwilowe obliczenie |
Robot r = new Robot();
r.Rusz(3); // Zuzyto 6, zostalo 4
r.Rusz(1); // Zuzyto 2, zostalo 2
Zwróć uwagę na drugie wywołanie: energia pamięta wynik pierwszego. Gdyby była zmienną lokalną, za każdym razem zaczynałaby od nowa. Pole to pamięć obiektu między wywołaniami metod.
Test na pole
Zadaj sobie pytanie: „czy ta wartość ma przetrwać do następnego wywołania metody?”. Jeśli tak — to pole. Jeśli służy tylko do policzenia czegoś tu i teraz — to zmienna lokalna. Trzymanie w polu czegoś, co jest potrzebne przez trzy wiersze, tylko zaśmieca obiekt.
Pole zawsze ma jakąś wartość
Zmienna lokalna bez przypisania nie da się użyć — kompilator zgłosi błąd CS0165. Z polem jest inaczej: pole dostaje wartość domyślną swojego typu, nawet jeśli nic mu nie przypiszesz.
| Typ pola | Wartość domyślna |
|---|---|
int, long, double, decimal | 0 |
bool | false |
char | znak o kodzie 0 |
string i inne typy referencyjne | null |
class Robot
{
public int Energia; // 0
public bool Wlaczony; // false
public string Nazwa; // null !
}
Robot r = new Robot();
Console.WriteLine(r.Energia); // 0
Console.WriteLine(r.Wlaczony); // False
// Console.WriteLine(r.Nazwa.Length); // NullReferenceException
Domyślny null to źródło kłopotów
Pole typu string bez wartości początkowej jest null, a nie pustym napisem. Pierwsze wywołanie jakiejkolwiek metody na nim kończy się wyjątkiem. Dlatego pola tekstowe warto od razu ustawiać: public string Nazwa = ""; — albo wymagać nazwy w konstruktorze, co zobaczysz w lekcji 40.
Inicjalizator pola
class Robot
{
private int energia = 10; // wartosc startowa
private string nazwa = "bez nazwy";
private readonly DateTime utworzony = DateTime.Now;
}
Wartość z inicjalizatora jest ustawiana przy każdym new, zanim wykona się cokolwiek innego. Dzięki temu obiekt od pierwszej chwili jest w sensownym stanie.
readonly — wartość ustalana raz
Pole oznaczone readonly wolno ustawić tylko w inicjalizatorze albo w konstruktorze. Później żadna metoda go nie zmieni — próba kończy się błędem kompilacji. Używamy go do rzeczy, które dla danego obiektu są stałe: numeru seryjnego, daty utworzenia, maksymalnej pojemności.
Każdy obiekt ma własny komplet pól
Robot a = new Robot();
Robot b = new Robot();
a.Rusz(4); // zuzywa energie robota a
Console.WriteLine(a.Energia); // 2
Console.WriteLine(b.Energia); // 10 - b nie ruszyl sie z miejsca
Klasa jest jedna, ale pola powstają osobno dla każdego obiektu. To właśnie dlatego dwóch graczy z poprzedniej lekcji mogło mieć różne punkty, choć opisywała ich ta sama klasa.
Wyjątek od tej zasady
Pole oznaczone static należy do klasy, a nie do obiektu — istnieje w jednym egzemplarzu, wspólnym dla wszystkich. Przydaje się na przykład do liczenia, ile obiektów utworzono. To temat lekcji 44; do tego czasu wszystkie pola piszemy bez static.
Nazewnictwo pól
| Zapis | Kiedy stosowany |
|---|---|
private int energia; | pole prywatne — camelCase, tak piszemy w tym kursie |
private int _energia; | ten sam zapis z podkreśleniem; częsty w projektach zespołowych, bo od razu widać, że to pole |
public int Energia; | pole publiczne — PascalCase; w praktyce zastępowane właściwością z lekcji 41 |
Przykład: robot, który pamięta
class Robot
{
private int energia = 10;
private int przejechane = 0;
private readonly string nazwa;
public Robot(string nazwa)
{
this.nazwa = nazwa; // 'this' rozroznia pole od parametru
}
public void Rusz(int odleglosc)
{
int koszt = odleglosc * 2; // zmienna lokalna - znika po tej metodzie
if (koszt > energia)
{
Console.WriteLine($"{nazwa}: za malo energii na {odleglosc} m");
return;
}
energia -= koszt;
przejechane += odleglosc;
Console.WriteLine($"{nazwa}: przejechal {odleglosc} m, energia {energia}");
}
public void Naladuj()
{
energia = 10;
Console.WriteLine($"{nazwa}: naladowany do {energia}");
}
public string Raport()
{
return $"{nazwa}: lacznie {przejechane} m, energia {energia}";
}
}
class Program
{
static void Main()
{
Robot alfa = new Robot("Alfa");
Robot beta = new Robot("Beta");
alfa.Rusz(3);
alfa.Rusz(1);
alfa.Rusz(5); // za malo energii
beta.Rusz(2);
Console.WriteLine();
Console.WriteLine(alfa.Raport());
Console.WriteLine(beta.Raport());
Console.WriteLine();
alfa.Naladuj();
Console.WriteLine(alfa.Raport());
}
}
Alfa: przejechal 3 m, energia 4
Alfa: przejechal 1 m, energia 2
Alfa: za malo energii na 5 m
Beta: przejechal 2 m, energia 6
Alfa: lacznie 4 m, energia 2
Beta: lacznie 2 m, energia 8
Alfa: naladowany do 10
Alfa: lacznie 4 m, energia 10
Co dzieje się po kolei
energiaiprzejechaneto pola — pamiętają stan między wywołaniami.kosztto zmienna lokalna: powstaje i znika przy każdym wywołaniuRusz.- Trzecie wywołanie
alfa.Rusz(5)zostaje odrzucone, a pola nie zmieniają się ani o jednostkę. Sprawdzenie jest w środku obiektu, więc nikt nie może go pominąć. - Roboty są niezależne: Beta ma 8 jednostek energii, choć Alfa zjechała prawie do zera.
nazwajestreadonly— ustawiona raz w konstruktorze i już nie do zmiany. Żadna metoda nie może przemianować robota.- Po naładowaniu
energiawraca do 10, aleprzejechanezostaje — to dwa osobne pola o dwóch różnych rolach.
Co robi każdy użyty element
| Element | Znaczenie | Uwagi |
|---|---|---|
| pole | zmienna zadeklarowana w klasie, poza metodami | Żyje tak długo, jak obiekt. Widzą je wszystkie metody tego obiektu. |
| zmienna lokalna | zmienna w ciele metody | Znika po zakończeniu metody. Bez przypisania nie da się jej użyć (CS0165). |
| parametr | zmienna w nawiasie metody | Dostaje wartość przy wywołaniu; istnieje przez jedno wywołanie. |
private int x = 5; | inicjalizator pola | Ustawia wartość przy każdym new, przed wykonaniem konstruktora. |
readonly | pole ustalane raz | Zapis tylko w inicjalizatorze albo konstruktorze; później błąd kompilacji. |
| wartość domyślna | 0, false, null | Pole ma ją zawsze, nawet bez inicjalizatora — także niechciane null. |
this.nazwa = nazwa; | rozróżnienie pola i parametru | Po lewej pole obiektu, po prawej parametr o tej samej nazwie. |
Zanim utkniesz
| Zapis | Problem |
|---|---|
| Pole zadeklarowane wewnątrz metody | To zmienna lokalna — znika po zakończeniu metody, a obiekt niczego nie pamięta. Sprawdź, czy deklaracja jest poza klamrami metod. |
public string Nazwa; bez wartości | Pole jest null; pierwsze Nazwa.Length daje NullReferenceException. |
nazwa = nazwa; w konstruktorze | Parametr przypisany sam do siebie — pole zostaje puste. Potrzebne jest this.nazwa = nazwa;. |
Zmiana pola readonly w metodzie | CS0191. Jeśli wartość ma się zmieniać, nie może być readonly. |
| Trzymanie wyniku pośredniego w polu | Obiekt „pamięta” coś, czego pamiętać nie powinien; łatwo o wynik z poprzedniego wywołania. Do obliczeń służą zmienne lokalne. |
| Pola publiczne w każdej klasie | Każdy może je zmienić na dowolną wartość, omijając sprawdzenia. Lekcje 39 i 41 pokazują, jak to zamknąć. |
Zadania
Napisz klasę Licznik z metodą Zwieksz(). Najpierw zadeklaruj licznik jako zmienną lokalną w metodzie i wywołaj metodę trzy razy. Zapisz wynik. Potem przenieś licznik do pola i powtórz. Wyjaśnij różnicę w komentarzu.
Napisz klasę z czterema polami publicznymi typów int, double, bool i string, nie nadając im wartości. Utwórz obiekt i wypisz wszystkie cztery. Zapisz, które wypisanie skończyło się wyjątkiem i dlaczego.
Napisz klasę Konto z polem saldo ustawionym na 0 w inicjalizatorze oraz metodami Wplata i Stan. Wykonaj trzy wpłaty i pokaż, że saldo się kumuluje.
Do klasy Konto dodaj pole readonly string numer, ustawiane w konstruktorze. Spróbuj zmienić je w metodzie i zapisz treść błędu kompilatora. Utwórz dwa konta o różnych numerach i pokaż, że ich salda są niezależne.
Napisz klasę Koszyk z polami przechowującymi liczbę pozycji i łączną wartość oraz metodą Dodaj(decimal cena, int sztuki). Wewnątrz metody wartość pozycji policz w zmiennej lokalnej. Uzasadnij w komentarzu, dlaczego ta wartość nie jest polem.
Napisz klasę Automat pamiętającą stan kasy, liczbę sprzedanych napojów i cenę jednostkową ustawianą w konstruktorze jako readonly. Metoda Wrzuc(decimal kwota) ma przyjmować monety, a Kup() wydawać napój wraz z resztą, gdy wrzucona kwota wystarcza, albo odmawiać z komunikatem. Po każdej sprzedaży wrzucona kwota wraca do zera. Sprawdź program na czterech sytuacjach, w tym na zbyt małej kwocie i na dwóch zakupach pod rząd.
Co trzeba zapamiętać
- Pole żyje tak długo jak obiekt; zmienna lokalna i parametr — przez jedno wywołanie metody.
- Pole to pamięć obiektu między wywołaniami metod.
- Pole zawsze ma wartość: domyślnie 0,
falsealbonull. - Pole typu
stringbez inicjalizatora jestnull, a nie pustym napisem. - Inicjalizator pola wykonuje się przy każdym
new, przed konstruktorem. readonlypozwala ustawić wartość tylko w inicjalizatorze albo konstruktorze.- Każdy obiekt ma własny komplet pól — z wyjątkiem pól
staticz lekcji 44.
Dokumentacja: Microsoft Learn — temat tej lekcji.