Właściwości — wygodny odczyt, kontrolowany zapis
Pole publiczne jest wygodne, ale przyjmie minus czterysta stopni. Para metod pilnuje zakresu, lecz użycie robi się rozwlekłe. Właściwość daje jedno i drugie.
Czego się dziś nauczysz
- Zamienisz pole publiczne na właściwość automatyczną.
- Dodasz do zapisu kontrolę wartości i użyjesz słowa
value. - Ograniczysz zapis do wnętrza klasy przez
private set. - Napiszesz właściwość wyliczaną w zapisie ze strzałką.
- Rozstrzygniesz, kiedy lepsza jest właściwość, a kiedy metoda.
Przygotowanie: lekcje 01–40. Przewidywany czas: 45–90 minut z zadaniami. Przykłady wymagają .NET 8 lub nowszego, z włączonymi ImplicitUsings i Nullable.
Pole jest wygodne, metoda bezpieczna
Termostat ma przechowywać zadaną temperaturę. Pole publiczne jest wygodne w użyciu, ale nie pilnuje niczego:
class Termostat
{
public int Temperatura;
}
Termostat t = new Termostat();
t.Temperatura = 22; // wygodnie
t.Temperatura = -400; // ...i nic tego nie powstrzyma
Para metod z lekcji 39 pilnuje zakresu, ale użycie robi się rozwlekłe:
class Termostat
{
private int temperatura = 20;
public int Odczytaj() { return temperatura; }
public void Ustaw(int wartosc)
{
if (wartosc < 5 || wartosc > 30) return;
temperatura = wartosc;
}
}
t.Ustaw(22);
Console.WriteLine(t.Odczytaj());
Właściwość łączy oba podejścia: w użyciu wygląda jak pole, a w środku wykonuje kod.
class Termostat
{
private int temperatura = 20;
public int Temperatura
{
get { return temperatura; }
set
{
if (value < 5 || value > 30) return;
temperatura = value;
}
}
}
Termostat t = new Termostat();
t.Temperatura = 22; // wyglada jak pole
Console.WriteLine(t.Temperatura); // 22
t.Temperatura = -400; // odrzucone przez set
Console.WriteLine(t.Temperatura); // 22 - bez zmian
Słowo value
W bloku set zmienna value pojawia się sama — to wartość stojąca po prawej stronie znaku równości w miejscu użycia. Nie deklarujemy jej i nie wolno nazwać inaczej.
Cztery zapisy, które trzeba znać
1. Właściwość automatyczna
Gdy nie ma czego sprawdzać, kompilator utworzy pole za ciebie:
public string Imie { get; set; } = "";
Pole istnieje, ale jest ukryte i nie ma nazwy, do której dałoby się sięgnąć.
2. Tylko do odczytu z zewnątrz
public int Punkty { get; private set; }
public void Dodaj(int ile)
{
if (ile > 0) Punkty += ile; // zapis z wnetrza klasy - wolno
}
Odczyt dla wszystkich, zapis tylko dla metod tej klasy. Najczęstszy zapis w całym kursie.
3. Ustawiana raz, przy tworzeniu
public string Numer { get; }
public Bilet(string numer)
{
Numer = numer; // wolno tylko tutaj albo w inicjalizatorze
}
4. Wyliczana — sam get
private decimal cena;
private int sztuki;
public decimal Razem
{
get { return cena * sztuki; }
}
// zapis skrocony, dokladnie to samo:
public decimal RazemKrocej => cena * sztuki;
Taka właściwość niczego nie przechowuje. Liczy wynik przy każdym odczycie — dlatego zawsze jest aktualna.
| Zapis | Odczyt | Zapis wartości | Kiedy |
|---|---|---|---|
{ get; set; } | wszędzie | wszędzie | prosta dana bez reguł |
{ get; private set; } | wszędzie | tylko w klasie | wartość zmieniana wyłącznie metodami |
{ get; } | wszędzie | tylko konstruktor | wartość ustalana raz |
{ get { … } set { … } } | wszędzie | przez kod sprawdzający | gdy trzeba pilnować zakresu |
=> wyrażenie | wszędzie | brak | wartość wyliczana z innych |
Właściwość czy metoda
Skoro właściwość może wykonywać kod, gdzie przebiega granica między nią a metodą? Reguła jest prosta: właściwość opisuje cechę, metoda opisuje czynność.
| Zapis | Dobry wybór? | Dlaczego |
|---|---|---|
gracz.Punkty | właściwość | to cecha gracza, odczyt jest natychmiastowy |
gracz.Opis | właściwość | krótkie sklejenie dwóch pól |
gracz.Dodaj(5) | metoda | czynność, która coś zmienia |
plik.WczytajWszystko | metoda | trwa długo i może się nie udać |
lista.Count | właściwość | gotowa liczba, nic nie liczy od nowa |
tekst.ToUpper() | metoda | tworzy nowy obiekt |
Właściwość nie powinna zaskakiwać
Odczyt obiekt.Cecha wygląda niewinnie, więc nie może kosztować sekundy ani zmieniać stanu obiektu. Jeśli operacja czyta plik, łączy się z siecią albo może zgłosić wyjątek — to metoda, nawet jeśli składniowo dałoby się zapisać ją jako właściwość.
Pułapka nieskończonej rekurencji
Zapis get { return Temperatura; } — z wielkiej litery, czyli odwołanie do samej siebie — tworzy nieskończoną pętlę i kończy się StackOverflowException. Właściwość ma zwracać pole, czyli temperatura z małej.
Przykład: termostat i bilet
class Termostat
{
private int temperatura = 20;
public string Nazwa { get; } // ustawiana raz
public int LiczbaZmian { get; private set; } // zapis tylko z wnetrza
public Termostat(string nazwa)
{
Nazwa = nazwa;
}
public int Temperatura // z kontrola zakresu
{
get { return temperatura; }
set
{
if (value < 5 || value > 30)
{
Console.WriteLine($"{Nazwa}: odrzucono {value} stopni");
return;
}
temperatura = value;
LiczbaZmian++;
}
}
public bool Chlodno => temperatura < 18; // wyliczana
public string Opis => $"{Nazwa}: {temperatura} st. ({LiczbaZmian} zmian)";
}
class Program
{
static void Main()
{
Termostat salon = new Termostat("Salon");
salon.Temperatura = 22;
salon.Temperatura = 45; // odrzucone
salon.Temperatura = 16;
Console.WriteLine(salon.Opis);
Console.WriteLine($"Chlodno: {salon.Chlodno}");
// salon.Nazwa = "Kuchnia"; // CS0200 - brak settera
// salon.LiczbaZmian = 100; // CS0272 - setter jest prywatny
}
}
Salon: odrzucono 45 stopni
Salon: 16 st. (2 zmian)
Chlodno: True
Co dzieje się po kolei
- Trzy przypisania, ale
LiczbaZmianwynosi 2 — odrzucona wartość nie zwiększyła licznika, bosetwychodzi przed jego zmianą. Chlodnonie jest przechowywane. Po ustawieniu 16 stopni odpowiedź zmienia się sama, bo liczy się przy każdym odczycie.Opiskorzysta zNazwaiLiczbaZmian— właściwość może używać innych właściwości.- Dwa zakomentowane wiersze pokazują dwa różne komunikaty: CS0200 przy braku settera i CS0272, gdy setter istnieje, ale jest prywatny.
Co robi każdy użyty element
| Element | Znaczenie | Uwagi |
|---|---|---|
get | akcesor odczytu | Musi zwrócić wartość typu właściwości. |
set | akcesor zapisu | Nowa wartość jest dostępna pod nazwą value. |
value | wartość przypisywana | Istnieje tylko w bloku set; nazwy nie wolno zmienić. |
{ get; set; } | właściwość automatyczna | Kompilator tworzy ukryte pole. |
{ get; private set; } | zapis tylko w klasie | Najczęstszy wybór, gdy wartość zmieniają metody. |
{ get; } | tylko do odczytu | Ustawiana w konstruktorze albo w inicjalizatorze. |
=> wyrażenie | właściwość wyliczana | Skrót zapisu get { return …; }. |
init | zapis tylko przy tworzeniu | Pozwala użyć inicjalizatora obiektu, a potem blokuje zmiany. |
| CS0200 / CS0272 | komunikaty | Brak settera / setter niedostępny w tym miejscu. |
Zanim utkniesz
| Zapis | Problem |
|---|---|
get { return Temperatura; } | Odwołanie do samej siebie — nieskończona rekurencja i StackOverflowException. Zwracaj pole z małej litery. |
set { temperatura = temperatura; } | Przypisanie pola do siebie. Nowa wartość kryje się pod nazwą value. |
public int Punkty { get; set; } plus metoda sprawdzająca | Reguła da się ominąć przypisaniem. Setter musi być private. |
| Publiczne pole obok właściwości o tej samej roli | Dwa wejścia do tej samej danej; jedno z nich na pewno ominie sprawdzenia. |
| Właściwość czytająca plik | Odczyt cechy nie powinien trwać ani zawodzić. To zadanie dla metody. |
{ get; } ustawiane w metodzie | CS0200 — wolno tylko w konstruktorze albo w inicjalizatorze pola. |
Zadania
Weź klasę Ksiazka z publicznymi polami Tytul i Rok. Zamień je na właściwości automatyczne i sprawdź, że program działa tak samo. Zapisz w komentarzu, co w kodzie użytkownika klasy musiałeś zmienić.
Napisz klasę Licznik z właściwością Wartosc mającą publiczny get i prywatny set oraz metodami Zwieksz i Zeruj. Spróbuj przypisać wartość z Main i zapisz numer błędu.
Napisz klasę Ocena z właściwością przyjmującą wyłącznie liczby od 1 do 6. Wartość spoza zakresu ma być odrzucona z komunikatem, a poprzednia zachowana. Sprawdź dla 0, 3, 6 i 7.
Do klasy Prostokat z właściwościami Szerokosc i Wysokosc dodaj wyliczane Pole i Obwod w zapisie ze strzałką. Pokaż, że po zmianie boku oba wyniki zmieniają się same.
Napisz klasę Bilet z właściwością Numer ustawianą wyłącznie w konstruktorze oraz właściwością Cena z kontrolą wartości ujemnych. Spróbuj zmienić numer po utworzeniu obiektu i zapisz komunikat kompilatora.
Napisz klasę Konto, w której Saldo ma publiczny odczyt i prywatny zapis, a operacje wykonują metody Wplata i Wyplata. Dodaj właściwości wyliczane: CzyNaMinusie oraz LiczbaOperacji. Zadbaj o to, żeby żadna droga z zewnątrz nie pozwalała ustawić salda wprost, i uzasadnij w komentarzu, dlaczego LiczbaOperacji nie ma settera.
Co trzeba zapamiętać
- Właściwość wygląda w użyciu jak pole, a w środku wykonuje kod.
- W bloku
setnowa wartość kryje się pod nazwąvalue. { get; private set; }pozwala odczytać wszystkim, a zmienić tylko metodom klasy.{ get; }ustawia się w konstruktorze i już nigdy więcej.- Właściwość wyliczana nie przechowuje nic — liczy przy każdym odczycie.
- Właściwość opisuje cechę; jeśli operacja trwa, zawodzi albo zmienia stan, to metoda.
Dokumentacja: Microsoft Learn — temat tej lekcji.