1. Wprowadzenie – Czym są właściwości?
Właściwości to członkowie klasy, które pozwalają nam kontrolować dostęp do danych. Wyobraź sobie, że masz dom z drzwiami wejściowymi. Ty decydujesz, kto może wejść (dostęp do pisania), kto może wyjść (dostęp do czytania), a możesz też postawić ochroniarza, który sprawdzi, czy osoba ma uprawnienia, zanim wpuści ją do domu.
Właściwości działają dokładnie tak samo – są „strażnikami” dostępu do danych prywatnych.
2. Problem przed właściwościami – Bezpośredni dostęp do pól
Zanim nauczymy się właściwości, zobaczmy, dlaczego są potrzebne.
Zły sposób – Pole publiczne:
public class Uczeń
{
public int Wiek; // Pole publiczne - BAD! 😟
}
// W programie:
var uczeń = new Uczeń();
uczeń.Wiek = -5; // Nikt nie zabrania! Wiek ujemny?
uczeń.Wiek = 150; // Lub 150 lat? To bez sensu!Problem: Każdy może przypisać jakąkolwiek wartość, nawet głupią.
3. Rozwiązanie – Metody gettera i settera
Stare podejście (przed właściwościami):
public class Uczeń
{
private int wiek; // Pole prywatne
// Getter (czytanie)
public int GetWiek()
{
return wiek;
}
// Setter (pisanie)
public void SetWiek(int nowyWiek)
{
if (nowyWiek > 0 && nowyWiek < 120)
{
wiek = nowyWiek;
}
else
{
Console.WriteLine("Wiek musi być między 1 a 119!");
}
}
}
// Użycie:
var uczeń = new Uczeń();
uczeń.SetWiek(16); // OK ✓
uczeń.SetWiek(-5); // Komunikat: "Wiek musi być między 1 a 119!" ✗Zaleta: Kontrolujemy, co przypisujemy! Wada: Kod jest długi i czasami mamy wrażenie, że coś wyglądałoby ładniej.
4. Nowoczesne rozwiązanie – Właściwości (Properties)
C# daje nam syntaktyczny cukier – właściwości, które działają jak pola, ale wewnątrz mają getter i setter:
Właściwość z pełną kontrolą:
public class Uczeń
{
private int _wiek; // Pole prywatne (z podkreśleniem)
// Właściwość publiczna
public int Wiek
{
get
{
return _wiek;
}
set
{
if (value > 0 && value < 120)
{
_wiek = value;
}
else
{
Console.WriteLine("Wiek musi być między 1 a 119!");
}
}
}
}
// Użycie - wyglądało jak zwyczajne pole!
var uczeń = new Uczeń();
uczeń.Wiek = 16; // Setter się uruchamia
int wiekUcznia = uczeń.Wiek; // Getter się uruchamia
Console.WriteLine(wiekUcznia); // Wypisuje: 16Magię działa! Przypisujemy i czytamy jak zwyczajne pole, ale wewnątrz to metody!
5. Krótka forma – Property Auto-Implemented
Gdy nie potrzebujemy żadnej logiki (żadnych walidacji), możemy pisać krócej:
public class Uczeń
{
// Krótka forma - kompilator sam tworzy pole prywatne!
public string Imię { get; set; }
public string Nazwisko { get; set; }
public int Wiek { get; set; }
}
// Użycie:
var uczeń = new Uczeń();
uczeń.Imię = "Jan";
uczeń.Nazwisko = "Kowalski";
uczeń.Wiek = 17;
Console.WriteLine($"{uczeń.Imię} {uczeń.Nazwisko}, wiek: {uczeń.Wiek}");
// Wypisuje: Jan Kowalski, wiek: 17Uwaga: Kompilator automatycznie tworzy prywatne pole w tle. My tego nie widzimy, ale ono tam jest!
6. Różne rodzaje właściwości
1. Tylko do czytania (read-only)
public class Uczeń
{
private string _numer;
public string Numer
{
get { return _numer; }
// Brak settera - nie można zmienić!
}
public Uczeń(string numer)
{
_numer = numer; // Tylko w konstruktorze możemy ustawić
}
}
// Użycie:
var uczeń = new Uczeń("2024/001");
Console.WriteLine(uczeń.Numer); // OK: 2024/001
// uczeń.Numer = "2024/002"; // BŁĄD! Nie można przypisaćTylko do pisania (write-only)
public class KontoBankowe
{
private string _pin;
public string PIN
{
set { _pin = value; }
// Brak gettera - nie możemy czytać!
}
}
// Użycie:
var konto = new KontoBankowe();
konto.PIN = "1234"; // OK
// string pin = konto.PIN; // BŁĄD! Nie można czytaćZ różnymi poziomami dostępu
public class Produkt
{
private decimal _cena;
public decimal Cena
{
get { return _cena; }
private set { _cena = value; } // Setter tylko dla klasy
}
// Metoda wewnątrz klasy
public void ZmienCenę(decimal nowaCena)
{
if (nowaCena > 0)
{
Cena = nowaCena; // OK - wewnątrz klasy
}
}
}
// Użycie:
var produkt = new Produkt();
// produkt.Cena = 100; // BŁĄD! Setter jest prywatny
produkt.ZmienCenę(100); // OK ✓7. Praktyczne przykłady – Właściwości z logiką
Przykład 1: Koło (właściwość obliczana)
public class Koło
{
private double _promień;
public double Promień
{
get { return _promień; }
set
{
if (value > 0)
{
_promień = value;
}
}
}
// Właściwość, która się oblicza (nie ma settera!)
public double Obwód
{
get { return 2 * Math.PI * _promień; }
}
public double Pole
{
get { return Math.PI * _promień * _promień; }
}
}
// Użycie:
var koło = new Koło();
koło.Promień = 5;
Console.WriteLine($"Obwód: {koło.Obwód:F2}"); // Obwód: 31,42
Console.WriteLine($"Pole: {koło.Pole:F2}"); // Pole: 78,54Przykład 2: Konto bankowe
public class Konto
{
private decimal _saldo;
public string Właściciel { get; set; }
public decimal Saldo
{
get { return _saldo; }
private set { _saldo = value; }
}
public void Wpłata(decimal kwota)
{
if (kwota > 0)
{
Saldo += kwota;
Console.WriteLine($"Wpłacono {kwota} zł. Nowe saldo: {Saldo} zł");
}
else
{
Console.WriteLine("Kwota musi być dodatnia!");
}
}
public void Wypłata(decimal kwota)
{
if (kwota > 0 && kwota <= Saldo)
{
Saldo -= kwota;
Console.WriteLine($"Wypłacono {kwota} zł. Nowe saldo: {Saldo} zł");
}
else if (kwota > Saldo)
{
Console.WriteLine("Niewystarczające środki!");
}
}
}
// Użycie:
var konto = new Konto { Właściciel = "Jan Kowalski" };
konto.Wpłata(1000); // Wpłacono 1000 zł. Nowe saldo: 1000 zł
konto.Wypłata(300); // Wypłacono 300 zł. Nowe saldo: 700 zł
konto.Wypłata(1000); // Niewystarczające środki!
Console.WriteLine($"Saldo {konto.Właściciel}: {konto.Saldo} zł");Przykład 3: Studenci i oceny
public class Estudiante
{
private List<int> _oceny = new List<int>();
public string Imię { get; set; }
public List<int> Oceny
{
get { return _oceny; }
}
public void DodajOcenę(int ocena)
{
if (ocena >= 1 && ocena <= 6)
{
_oceny.Add(ocena);
Console.WriteLine($"Dodano ocenę: {ocena}");
}
else
{
Console.WriteLine("Ocena musi być między 1 a 6!");
}
}
// Właściwość tylko do czytania - oblicza średnią
public double ŚredniaOcen
{
get
{
if (_oceny.Count == 0) return 0;
return _oceny.Average();
}
}
}
// Użycie:
var student = new Estudiante { Imię = "Marta" };
student.DodajOcenę(5);
student.DodajOcenę(4);
student.DodajOcenę(5);
student.DodajOcenę(3);
Console.WriteLine($"{student.Imię}, średnia: {student.ŚredniaOcen:F2}");
// Marta, średnia: 4,258. Właściwości zainicjalizowane (C# 6.0+)
public class Osoba
{
// Właściwość z wartością domyślną
public string Imię { get; set; } = "Nieznane";
public string Nazwisko { get; set; } = "Nieznane";
public DateTime DataUrodzenia { get; set; } = DateTime.Now;
// Tylko do czytania, ale z wartością domyślną
public string PełneImię
{
get { return $"{Imię} {Nazwisko}"; }
}
}
// Użycie:
var osoba = new Osoba(); // Wszystkie wartości domyślne
Console.WriteLine(osoba.PełneImię); // Nieznane Nieznane
osoba.Imię = "Paweł";
osoba.Nazwisko = "Nowak";
Console.WriteLine(osoba.PełneImię); // Paweł Nowak9. Podsumowanie – Kiedy używać właściwości?
| Situation | Co robić? |
|---|---|
| Proste pole bez walidacji | Auto-property: public int Wiek { get; set; } |
| Pole z walidacją | Property z logiką w setterze |
| Dane tylko do czytania | Property z samym getterem |
| Obliczane wartości | Property z logiką w getterze |
| Kontrola dostępu | Private/protected settery |
10. Ćwiczenia dla uczniów
Ćwiczenie 1: Klasa Samochód
Stwórz klasę Samochód z właściwościami:
Marka(string, auto-property)Prędkość(int, z walidacją: maks 300)Paliwo(decimal, getter tylko – zmienia się w metodzieJedź)- Metoda
Jedź(int km)– zmniejsza paliwo
Ćwiczenie 2: Klasa Gracz
Stwórz klasę Gracz z właściwościami:
Nazwa(auto-property)Punkty(int, getter publiczny, setter prywatny)- Metoda
ZdobądźPunkty(int punkty)– zwiększa punkty - Właściwość
Poziom(obliczana: Punkty / 100)
Ćwiczenie 3: Klasa Temperatura
Stwórz klasę Temperatura:
- Przechowuje temperaturę w Celsjuszach
- Właściwość
Celsius(int z walidacją) - Właściwość
Fahrenheit(obliczana, tylko do czytania)