Właściwości w C# – get i set

Nauczysz się kontrolować dostęp do danych w klasach za pomocą właściwości. Poznasz auto-properties, walidację i właściwości obliczane.

get set auto-property enkapsulacja walidacja
01

Czym są właściwości?

Właściwości to członkowie klasy, które pozwalają nam kontrolować dostęp do danych. Są „strażnikami” dostępu do pól prywatnych.

Wyobraź sobie, że masz dom z drzwiami wejściowymi. Ty decydujesz:

  • Kto może wejść – dostęp do pisania (set)
  • Kto może wyjść – dostęp do czytania (get)
  • Ochroniarz sprawdza uprawnienia – walidacja danych
Pole prywatne
_wiek
Właściwość
get / set
Świat zewnętrzny
bezpieczny dostęp
Enkapsulacja

Właściwości to kluczowy element enkapsulacji – ukrywania wewnętrznych danych klasy i udostępniania tylko kontrolowanego interfejsu.

02

Problem z polami publicznymi

Zanim poznamy właściwości, zobaczmy dlaczego są potrzebne.

❌ Zły sposób – pole publiczne

cs/PolePubliczne.cs C#
public class Uczen
{
    public int Wiek;  // Pole publiczne - BAD! 😟
}

// W programie:
var uczen = new Uczen();
uczen.Wiek = -5;    // Nikt nie zabrania! Wiek ujemny? 🤔
uczen.Wiek = 150;   // Lub 150 lat? To bez sensu!
Problem

Każdy może przypisać jakąkolwiek wartość, nawet całkowicie bezsensowną. Brak kontroli = błędy w programie!

03

Stare podejście – metody Get/Set

Przed właściwościami programiści pisali osobne metody do czytania i pisania danych.

cs/MetodyGetSet.cs C#
public class Uczen
{
    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 uczen = new Uczen();
uczen.SetWiek(16);   // OK ✓
uczen.SetWiek(-5);   // "Wiek musi być między 1 a 119!" ✗

✅ Zaleta

Kontrolujemy, co przypisujemy! Walidacja działa.

❌ Wada

Kod jest długi i wygląda mniej naturalnie niż zwykłe pole.

04

Nowoczesne rozwiązanie – Właściwości

C# daje nam syntaktyczny cukier – właściwości, które działają jak pola, ale wewnątrz mają getter i setter.

cs/Wlasciwosc.cs C#
public class Uczen
{
    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 jak zwyczajne pole!
var uczen = new Uczen();
uczen.Wiek = 16;             // Setter się uruchamia
int wiekUcznia = uczen.Wiek; // Getter się uruchamia
Console.WriteLine(wiekUcznia); // Wypisuje: 16
Słowo kluczowe „value”

W setterze value to specjalna zmienna, która zawiera wartość przypisywaną do właściwości. Gdy piszesz uczen.Wiek = 16, to value będzie równe 16.

Magia właściwości

Przypisujemy i czytamy jak zwyczajne pole, ale wewnątrz wykonują się metody z walidacją!

uczen.Wiek = 16
przypisanie
set { }
walidacja
_wiek = 16
zapis do pola
05

Auto-properties (krótka forma)

Gdy nie potrzebujemy żadnej logiki (walidacji), możemy pisać znacznie krócej.

cs/AutoProperty.cs C#
public class Uczen
{
    // Krótka forma – kompilator sam tworzy pole prywatne!
    public string Imie { get; set; }
    public string Nazwisko { get; set; }
    public int Wiek { get; set; }
}

// Użycie:
var uczen = new Uczen();
uczen.Imie = "Jan";
uczen.Nazwisko = "Kowalski";
uczen.Wiek = 17;

Console.WriteLine($"{uczen.Imie} {uczen.Nazwisko}, wiek: {uczen.Wiek}");
// Wypisuje: Jan Kowalski, wiek: 17
Co robi kompilator?

Kompilator automatycznie tworzy prywatne pole w tle. My tego nie widzimy, ale ono tam jest! To tak jakbyś napisał pełną wersję z get/set.

Pełna forma (długa)

private int _wiek;
public int Wiek { get { return _wiek; } set { _wiek = value; } }

Auto-property (krótka)

public int Wiek { get; set; }
Jedna linia zamiast czterech!

06

Różne rodzaje właściwości

1. Tylko do czytania (read-only)

Właściwość bez settera – można tylko odczytać, nie można zmienić z zewnątrz.

cs/ReadOnly.cs C#
public class Uczen
{
    private string _numer;

    public string Numer
    {
        get { return _numer; }
        // Brak settera – nie można zmienić!
    }

    public Uczen(string numer)
    {
        _numer = numer;  // Tylko w konstruktorze możemy ustawić
    }
}

// Użycie:
var uczen = new Uczen("2024/001");
Console.WriteLine(uczen.Numer);  // OK: 2024/001
// uczen.Numer = "2024/002";      // BŁĄD! Nie można przypisać

2. Z różnymi poziomami dostępu

Getter publiczny, ale setter prywatny – z zewnątrz można tylko czytać.

cs/PrivateSetter.cs C#
public class Produkt
{
    private decimal _cena;

    public decimal Cena
    {
        get { return _cena; }
        private set { _cena = value; }  // Setter tylko dla klasy
    }

    // Metoda wewnątrz klasy może używać private set
    public void ZmienCene(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.ZmienCene(100);   // OK ✓
Kiedy używać private set?

Gdy chcesz, żeby wartość mogła się zmieniać, ale tylko przez kontrolowane metody klasy (np. Wpłata(), Wypłata()), a nie przez bezpośrednie przypisanie.

07

Właściwości obliczane

Właściwość nie musi przechowywać danych – może je obliczać na podstawie innych pól!

Przykład 1: Koło

cs/Kolo.cs C#
public class Kolo
{
    private double _promien;

    public double Promien
    {
        get { return _promien; }
        set
        {
            if (value > 0)
            {
                _promien = value;
            }
        }
    }

    // Właściwości OBLICZANE – nie mają settera!
    public double Obwod
    {
        get { return 2 * Math.PI * _promien; }
    }

    public double Pole
    {
        get { return Math.PI * _promien * _promien; }
    }
}

// Użycie:
var kolo = new Kolo();
kolo.Promien = 5;
Console.WriteLine($"Obwód: {kolo.Obwod:F2}");   // Obwód: 31,42
Console.WriteLine($"Pole: {kolo.Pole:F2}");     // Pole: 78,54

Przykład 2: Konto bankowe

cs/Konto.cs C#
public class Konto
{
    private decimal _saldo;

    public string Wlasciciel { get; set; }

    public decimal Saldo
    {
        get { return _saldo; }
        private set { _saldo = value; }
    }

    public void Wplata(decimal kwota)
    {
        if (kwota > 0)
        {
            Saldo += kwota;
            Console.WriteLine($"Wpłacono {kwota} zł. Nowe saldo: {Saldo} zł");
        }
    }

    public void Wyplata(decimal kwota)
    {
        if (kwota > 0 && kwota <= Saldo)
        {
            Saldo -= kwota;
            Console.WriteLine($"Wypłacono {kwota} zł. Nowe saldo: {Saldo} zł");
        }
        else
        {
            Console.WriteLine("Niewystarczające środki!");
        }
    }
}

// Użycie:
var konto = new Konto { Wlasciciel = "Jan Kowalski" };
konto.Wplata(1000);     // Wpłacono 1000 zł. Nowe saldo: 1000 zł
konto.Wyplata(300);     // Wypłacono 300 zł. Nowe saldo: 700 zł
konto.Wyplata(1000);    // Niewystarczające środki!

Przykład 3: Student z ocenami

cs/Student.cs C#
public class Student
{
    private List<int> _oceny = new List<int>();

    public string Imie { get; set; }

    public List<int> Oceny
    {
        get { return _oceny; }
    }

    public void DodajOcene(int ocena)
    {
        if (ocena >= 1 && ocena <= 6)
        {
            _oceny.Add(ocena);
        }
    }

    // Właściwość OBLICZANA – średnia ocen
    public double SredniaOcen
    {
        get
        {
            if (_oceny.Count == 0) return 0;
            return _oceny.Average();
        }
    }
}

// Użycie:
var student = new Student { Imie = "Marta" };
student.DodajOcene(5);
student.DodajOcene(4);
student.DodajOcene(5);
student.DodajOcene(3);
Console.WriteLine($"{student.Imie}, średnia: {student.SredniaOcen:F2}");
// Marta, średnia: 4,25
08

Właściwości z wartościami domyślnymi

Od C# 6.0 możemy nadać właściwościom wartości domyślne bezpośrednio przy deklaracji.

cs/WartosciDomyslne.cs C#
public class Osoba
{
    // Właściwości z wartościami domyślnymi
    public string Imie { get; set; } = "Nieznane";
    public string Nazwisko { get; set; } = "Nieznane";
    public DateTime DataUrodzenia { get; set; } = DateTime.Now;

    // Właściwość tylko do czytania, obliczana
    public string PelneImie
    {
        get { return $"{Imie} {Nazwisko}"; }
    }
}

// Użycie:
var osoba = new Osoba();  // Wszystkie wartości domyślne
Console.WriteLine(osoba.PelneImie);  // Nieznane Nieznane

osoba.Imie = "Paweł";
osoba.Nazwisko = "Nowak";
Console.WriteLine(osoba.PelneImie);  // Paweł Nowak
09

Podsumowanie – kiedy czego używać?

SytuacjaCo użyć?Przykład
Proste pole bez walidacji Auto-property public int Wiek { get; set; }
Pole z walidacją Property z logiką w setterze set { if (value > 0) _wiek = value; }
Dane tylko do czytania Property z samym getterem public string Numer { get; }
Obliczane wartości Property z logiką w getterze get { return _oceny.Average(); }
Kontrola dostępu Private/protected setter public decimal Saldo { get; private set; }
Złota zasada

Domyślnie używaj auto-properties. Dodawaj logikę (walidację, obliczenia) tylko gdy jest potrzebna.

Zadania

Ćwiczenia dla uczniów

📝 Zadanie 1: Klasa Samochod

Stwórz klasę Samochod z właściwościami:

  • Marka – string, auto-property
  • Predkosc – int, z walidacją: maksymalnie 300 km/h
  • Paliwo – decimal, getter tylko (zmienia się w metodzie Jedz)

Dodaj metodę Jedz(int km) – zmniejsza paliwo o km * 0.08 (8L/100km).

💡 Podpowiedź: Paliwo nie może spaść poniżej 0!

📝 Zadanie 2: Klasa Gracz

Stwórz klasę Gracz z właściwościami:

  • Nazwa – auto-property
  • Punkty – int, getter publiczny, setter prywatny
  • Poziom – właściwość obliczana: Punkty / 100

Dodaj metodę ZdobadzPunkty(int punkty) – zwiększa punkty.

📝 Zadanie 3: Klasa Temperatura

Stwórz klasę Temperatura:

  • Przechowuje temperaturę w Celsjuszach (pole prywatne)
  • Celsius – właściwość z walidacją (min -273.15°C)
  • Fahrenheit – właściwość obliczana, tylko do czytania

Wzór: F = C × 9/5 + 32

⭐ Bonus: Dodaj właściwość Kelvin (obliczaną)

⭐ Zadanie 4: Klasa Prostokat

Stwórz klasę Prostokat z:

  • Szerokosc i Wysokosc – z walidacją (> 0)
  • Pole – właściwość obliczana (tylko getter)
  • Obwod – właściwość obliczana (tylko getter)
  • CzyKwadrat – właściwość bool, obliczana

⭐⭐ Zadanie 5: Klasa KontoUzytkownika

Stwórz klasę KontoUzytkownika z:

  • Login – read-only (ustawiany tylko w konstruktorze)
  • Email – z walidacją (musi zawierać @)
  • Haslo – write-only (tylko setter, nie można odczytać!)
  • DataRejestracji – auto-property z wartością domyślną DateTime.Now
  • DniOdRejestracji – właściwość obliczana

Dodaj metodę SprawdzHaslo(string haslo) zwracającą bool.