Pola i modyfikatory dostępu – schowki na dane

Nauczysz się przechowywać dane w klasach i kontrolować, kto ma do nich dostęp. To fundament programowania obiektowego!

pola public/private enkapsulacja static readonly/const
01

Co to są pola w klasie?

Pola to zmienne zdefiniowane wewnątrz klasy, które przechowują dane obiektu. Można je porównać do szuflad – każdy obiekt ma swoje własne szuflady z własnymi wartościami.

Analogia: Szafka ucznia

Wyobraź sobie szafki w szatni szkolnej:

  • Klasa = projekt szafki (jak ma wyglądać, jakie ma przegródki)
  • Obiekt = konkretna szafka ucznia (np. szafka Janka, szafka Ani)
  • Pola = przegródki w szafce (na książki, zeszyty, telefon)
  • Modyfikatory dostępu = kto może otwierać które przegródki
cs/Uczen.cs C#
public class Uczen
{
    // To są POLA klasy - "przegródki" każdego ucznia
    public string imie;           // Każdy może zobaczyć
    private string numerTelefonu; // Tylko sam uczeń ma dostęp
    public int wiek;
    private double srednia;
}
Pola vs zmienne lokalne

Zmienna lokalna – istnieje tylko wewnątrz metody, znika po jej zakończeniu.
Pole – istnieje tak długo, jak istnieje obiekt. Każda metoda w klasie ma do niego dostęp.

02

Pola instancji vs pola statyczne

Są dwa rodzaje pól: pola instancji (każdy obiekt ma swoją kopię) i pola statyczne (jedna wspólna wartość dla wszystkich obiektów).

Pola instancji (zwykłe)

Każdy obiekt ma własną kopię pola z własną wartością:

cs/Samochod.cs C#
public class Samochod
{
    // Pola instancji - każdy samochód ma SWOJE wartości
    public string marka;
    public int rocznik;
    public double przebieg;
}

// Użycie:
Samochod auto1 = new Samochod();
auto1.marka = "Toyota";
auto1.przebieg = 50000;

Samochod auto2 = new Samochod();
auto2.marka = "BMW";
auto2.przebieg = 80000;

Console.WriteLine(auto1.marka);  // "Toyota"
Console.WriteLine(auto2.marka);  // "BMW" – różne wartości!
auto1
marka = „Toyota”
przebieg = 50000
auto2
marka = „BMW”
przebieg = 80000

↑ Każdy obiekt ma własne kopie pól

Pola statyczne (static)

Jedna wartość wspólna dla WSZYSTKICH obiektów danej klasy:

cs/LicznikObiektow.cs C#
public class Produkt
{
    // Pole STATYCZNE – wspólne dla wszystkich produktów
    public static int iloscProduktow = 0;
    
    // Pola instancji – każdy produkt ma swoje
    public string nazwa;
    public double cena;
    
    // Konstruktor
    public Produkt(string nazwa, double cena)
    {
        this.nazwa = nazwa;
        this.cena = cena;
        iloscProduktow++;  // Zwiększamy WSPÓLNY licznik
    }
}

// Użycie:
Produkt p1 = new Produkt("Laptop", 3500);
Produkt p2 = new Produkt("Mysz", 50);
Produkt p3 = new Produkt("Klawiatura", 120);

// Dostęp przez NAZWĘ KLASY (nie przez obiekt!)
Console.WriteLine(Produkt.iloscProduktow);  // 3
p1
nazwa = „Laptop”
p2
nazwa = „Mysz”
p3
nazwa = „Klawiatura”
static
iloscProduktow = 3

↑ Wszystkie obiekty współdzielą jedno pole statyczne

Jak odwoływać się do pola statycznego?

Do pola statycznego odwołujemy się przez nazwę klasy, nie przez obiekt:
Produkt.iloscProduktow
p1.iloscProduktow – zadziała, ale to zły styl!

CechaPole instancjiPole statyczne (static)
Liczba kopiiKażdy obiekt ma swojąJedna dla całej klasy
Dostępobiekt.poleKlasa.pole
Przykładimię ucznia, cena produktulicznik obiektów, stałe
PamięćW każdym obiekcieRaz, niezależnie od obiektów
03

Modyfikatory dostępu – „poziomy bezpieczeństwa”

Modyfikatory dostępu określają, kto może odczytywać i zmieniać dane pole. To jak poziomy bezpieczeństwa w budynku.

public
Każdy
internal
Ten projekt
protected
Ta klasa + dzieci
private
Tylko ta klasa

Od najbardziej otwartego do najbardziej restrykcyjnego

public – dostęp dla wszystkich

cs/Public.cs C#
public class Osoba
{
    public string imie;   // Każdy może odczytać i zmienić
    public int wiek;
}

// Gdziekolwiek w kodzie:
Osoba jan = new Osoba();
jan.imie = "Jan";      // ✅ Działa
jan.wiek = -50;        // ✅ Działa... ale to błąd logiczny!
Problem z public

Każdy może wpisać dowolną wartość, nawet błędną (np. wiek ujemny). Brak kontroli nad danymi!

private – dostęp tylko z tej klasy

cs/Private.cs C#
public class Osoba
{
    private string imie;   // Tylko metody TEJ klasy mają dostęp
    private int wiek;
    
    // Metoda publiczna do ODCZYTU
    public string GetImie()
    {
        return imie;
    }
    
    // Metoda publiczna do ZAPISU z walidacją
    public void SetWiek(int nowyWiek)
    {
        if (nowyWiek >= 0 && nowyWiek <= 150)
        {
            wiek = nowyWiek;
        }
        else
        {
            Console.WriteLine("Nieprawidłowy wiek!");
        }
    }
}

// Użycie:
Osoba jan = new Osoba();
// jan.wiek = -50;      // ❌ BŁĄD KOMPILACJI! Pole jest private
jan.SetWiek(-50);       // ✅ Kompiluje się, ale wyświetli "Nieprawidłowy wiek!"
jan.SetWiek(25);        // ✅ Działa poprawnie
Domyślny modyfikator

Jeśli nie napiszesz żadnego modyfikatora, pole jest domyślnie private. Ale zawsze pisz modyfikator jawnie – kod jest wtedy czytelniejszy.

protected – ta klasa + klasy dziedziczące

cs/Protected.cs C#
public class Osoba
{
    protected string pesel;  // Ta klasa + klasy pochodne
}

public class Pracownik : Osoba  // Pracownik dziedziczy po Osoba
{
    public void WyswietlPesel()
    {
        Console.WriteLine(pesel);  // ✅ Działa – protected
    }
}

// Ale z zewnątrz:
Osoba jan = new Osoba();
// jan.pesel = "12345";  // ❌ BŁĄD! Nie jesteśmy w klasie Osoba ani jej dziecku

internal – dostęp z tego projektu (assembly)

cs/Internal.cs C#
public class Konfiguracja
{
    internal string connectionString;  // Dostęp z całego projektu
}
Co to jest projekt (assembly)?

W Visual Studio projekt to jeden plik .csproj. Wszystkie klasy w tym samym projekcie mają dostęp do pól internal. Klasy z innego projektu (np. biblioteki zewnętrznej) – nie mają.

W prostych programach (jeden projekt) internal działa podobnie jak public.

Tabela podsumowująca

ModyfikatorTa klasaKlasa pochodnaTen projektInny projekt
public
internal
protected
private
04

Enkapsulacja – dlaczego ukrywać dane?

Enkapsulacja (ang. encapsulation) to jedna z fundamentalnych zasad programowania obiektowego: ukrywamy dane (private), a udostępniamy kontrolowany interfejs (public).

Analogia: Bankomat

Bankomat to doskonały przykład enkapsulacji:

  • Ukryte (private): pieniądze wewnątrz, mechanizm wydawania, połączenie z bankiem
  • Dostępne (public): ekran, klawiatura, szczelina na kartę

Nie możesz sięgnąć ręką po pieniądze – musisz użyć interfejsu (wpisać PIN, wybrać kwotę).

Przykład bez enkapsulacji (źle)

cs/KontoBezEnkapsulacji.cs C#
public class KontoBankowe
{
    public double saldo;  // ❌ Każdy może zmienić!
}

// Problem:
KontoBankowe konto = new KontoBankowe();
konto.saldo = 1000;
konto.saldo = -99999;   // ❌ Ujemne saldo? Brak kontroli!
konto.saldo = 999999999; // ❌ Ktoś sobie "dopłacił"

Przykład z enkapsulacją (dobrze)

cs/KontoZEnkapsulacja.cs C#
public class KontoBankowe
{
    private double saldo;  // ✅ Ukryte – nikt nie zmieni bezpośrednio
    
    // Odczyt salda (każdy może zobaczyć)
    public double GetSaldo()
    {
        return saldo;
    }
    
    // Wpłata – kontrolowana operacja
    public void Wplata(double kwota)
    {
        if (kwota > 0)
        {
            saldo += kwota;
            Console.WriteLine($"Wpłacono {kwota} zł. Saldo: {saldo} zł");
        }
    }
    
    // Wypłata – z walidacją
    public bool Wyplata(double kwota)
    {
        if (kwota > 0 && kwota <= saldo)
        {
            saldo -= kwota;
            Console.WriteLine($"Wypłacono {kwota} zł. Saldo: {saldo} zł");
            return true;
        }
        Console.WriteLine("Brak środków lub nieprawidłowa kwota!");
        return false;
    }
}

// Użycie:
KontoBankowe konto = new KontoBankowe();
konto.Wplata(1000);      // Wpłacono 1000 zł. Saldo: 1000 zł
konto.Wyplata(200);      // Wypłacono 200 zł. Saldo: 800 zł
konto.Wyplata(9999);     // Brak środków lub nieprawidłowa kwota!
// konto.saldo = 99999;  // ❌ BŁĄD KOMPILACJI – pole jest private

❌ Bez enkapsulacji

public double saldo;

  • Każdy może zmienić na dowolną wartość
  • Brak historii operacji
  • Brak walidacji

✅ Z enkapsulacją

private double saldo;

  • Zmiana tylko przez Wplata/Wyplata
  • Możemy logować operacje
  • Walidacja kwot
Zasada enkapsulacji

Pola rób private, dostęp dawaj przez metody.
Dzięki temu masz kontrolę nad tym, co i jak jest zmieniane.

05

Pola readonly – ustaw raz, nie zmieniaj

Pole readonly można ustawić tylko raz – przy deklaracji lub w konstruktorze. Potem jest już tylko do odczytu.

cs/Readonly.cs C#
public class Osoba
{
    // readonly – ustawiamy w konstruktorze, potem nie można zmienić
    public readonly string pesel;
    public readonly DateTime dataUrodzenia;
    
    // Zwykłe pole – można zmieniać
    public string imie;
    
    public Osoba(string pesel, DateTime dataUrodzenia, string imie)
    {
        this.pesel = pesel;                 // ✅ OK – konstruktor
        this.dataUrodzenia = dataUrodzenia; // ✅ OK – konstruktor
        this.imie = imie;
    }
    
    public void ZmienImie(string noweImie)
    {
        imie = noweImie;    // ✅ OK – zwykłe pole
        // pesel = "nowy";   // ❌ BŁĄD! Pole readonly
    }
}

// Użycie:
Osoba jan = new Osoba("12345678901", new DateTime(2000, 1, 15), "Jan");
jan.imie = "Janek";  // ✅ OK
// jan.pesel = "99";   // ❌ BŁĄD KOMPILACJI!
Kiedy używać readonly?

Dla danych, które nie powinny się zmieniać po utworzeniu obiektu:

  • PESEL, numer dowodu – nie zmieniają się
  • Data urodzenia – nie zmieni się
  • ID obiektu w bazie danych
  • Data utworzenia obiektu
06

Pola const – stałe wartości

Pole const to stała – wartość znana już podczas kompilacji programu. Nigdy się nie zmienia.

cs/Const.cs C#
public class Matematyka
{
    // Stałe matematyczne – nigdy się nie zmienią
    public const double PI = 3.14159265359;
    public const double E = 2.71828182846;
}

public class Walidacja
{
    // Stałe ograniczenia
    public const int MIN_WIEK = 0;
    public const int MAX_WIEK = 150;
    public const int MIN_HASLO = 8;
    public const string DOMYSLNA_WALUTA = "PLN";
}

// Użycie – przez NAZWĘ KLASY (const jest automatycznie static)
Console.WriteLine(Matematyka.PI);        // 3.14159265359
Console.WriteLine(Walidacja.MAX_WIEK);   // 150

if (wiek > Walidacja.MAX_WIEK)
{
    Console.WriteLine("Nieprawidłowy wiek!");
}
Ograniczenia const

Wartość const musi być znana podczas kompilacji. Nie możesz użyć:

  • const DateTime teraz = DateTime.Now;
  • const string id = Guid.NewGuid().ToString();

Do takich przypadków użyj static readonly.

Konwencja nazewnictwa

Stałe const często zapisuje się WIELKIMI_LITERAMI z podkreślnikami:
MAX_WIEK, MIN_HASLO, DOMYSLNA_WALUTA

07

Porównanie: const vs readonly vs static readonly

Cechaconstreadonlystatic readonly
Kiedy ustawiamy?Przy kompilacjiW konstruktorze obiektuW konstruktorze statycznym lub przy deklaracji
Można zmienić?❌ Nigdy❌ Po konstruktorze – nie❌ Po konstruktorze – nie
Czy static?✅ Automatycznie❌ Nie (każdy obiekt ma swoją)✅ Tak
Typy danychTylko proste (int, string, double)DowolneDowolne
Przykład użyciaPI, MAX_WIEKPESEL, dataUrodzeniaDataUruchomienia, Config
cs/Porownanie.cs C#
public class Przyklad
{
    // const – wartość znana przy kompilacji
    public const int MAX = 100;
    
    // readonly – każdy obiekt ma swoją, ustawianą w konstruktorze
    public readonly Guid id;
    
    // static readonly – jedna dla całej klasy, może używać DateTime.Now
    public static readonly DateTime DataUruchomienia = DateTime.Now;
    
    public Przyklad()
    {
        id = Guid.NewGuid();  // Każdy obiekt dostaje unikalny ID
    }
}
08

Częste błędy

❌ Błąd 1: Wszystko public

❌ Źle

public class Osoba
{
    public string imie;
    public int wiek;
    public string haslo;
}

Każdy może zmienić hasło!

✅ Dobrze

public class Osoba
{
    public string imie;
    private int wiek;
    private string haslo;
}

Wrażliwe dane ukryte.

❌ Błąd 2: const bez wartości

❌ Źle

public const string nazwa;
// BŁĄD! const musi mieć wartość

✅ Dobrze

public const string NAZWA = "App";

❌ Błąd 3: Zmiana readonly poza konstruktorem

❌ Źle

public readonly string id;

public void Reset()
{
    id = "nowy"; // BŁĄD!
}

✅ Dobrze

public readonly string id;

public Klasa()
{
    id = "abc"; // OK w konstruktorze
}

❌ Błąd 4: Brak typu w static

❌ Źle

public static wiek = 25;
// BŁĄD! Brak typu

✅ Dobrze

public static int wiek = 25;
09

Podsumowanie

Modyfikatory dostępu

ModyfikatorKto ma dostęp?Kiedy używać?
publicWszyscyMetody interfejsu, stałe
privateTylko ta klasaDane wewnętrzne – domyślny wybór!
protectedTa klasa + dzieciGdy planujesz dziedziczenie
internalTen projektKlasy pomocnicze w bibliotece

Modyfikatory wartości

ModyfikatorCo oznacza?Przykład
staticWspólne dla wszystkich obiektówLicznik obiektów
readonlyMożna ustawić tylko raz (w konstruktorze)ID, data utworzenia
constStała znana przy kompilacjiPI, MAX_WIEK
Zasady do zapamiętania
  • Pola = szuflady obiektu – przechowują dane
  • private domyślnie – public tylko gdy naprawdę trzeba
  • Enkapsulacja = ukrywaj dane, udostępniaj metody
  • static = jedno dla wszystkich obiektów
  • const = nigdy się nie zmieni
  • readonly = można ustawić tylko raz
Zadania

Zadania praktyczne

📝 Zadanie 1: Klasa Pracownik

Utwórz klasę Pracownik z polami:

  • imie – publiczne
  • nazwisko – publiczne
  • pensja – prywatne (z metodami Get/Set i walidacją: min. 0)
  • dataZatrudnienia – readonly (ustawiane w konstruktorze)
  • MINIMALNA_PENSJA – const = 3490 (płaca minimalna)

💡 Podpowiedź: W metodzie SetPensja() sprawdź, czy nowa pensja >= MINIMALNA_PENSJA

📝 Zadanie 2: Licznik produktów

Utwórz klasę Produkt z:

  • Polem statycznym iloscProduktow – liczy ile produktów utworzono
  • Polem statycznym nastepneId – generuje kolejne ID
  • Polem readonly id – unikalne dla każdego produktu
  • Polami nazwa i cena

Każdy nowy produkt powinien automatycznie dostać kolejne ID.

💡 Podpowiedź: W konstruktorze: id = nastepneId++;

📝 Zadanie 3: Znajdź i popraw błędy

W tym kodzie jest 5 błędów. Znajdź je i popraw:

cs/Bledy.cs C#
public class Test
{
    public const string nazwa;           // Błąd 1
    public static liczba = 10;           // Błąd 2
    private readonly int id;
    
    public Test()
    {
        id = 1;
    }
    
    public void Zmien()
    {
        id = 99;                            // Błąd 3
        nazwa = "nowa";                     // Błąd 4
    }
}

class Program
{
    static void Main()
    {
        Test t = new Test();
        Console.WriteLine(t.id);            // Błąd 5
    }
}

💡 Podpowiedź: const musi mieć wartość, static potrzebuje typu, readonly nie można zmieniać poza konstruktorem, private nie jest dostępne z zewnątrz

⭐ Zadanie 4: Konto bankowe z enkapsulacją

Utwórz klasę KontoBankowe z pełną enkapsulacją:

  • numerKonta – readonly (generowany w konstruktorze)
  • saldo – private (dostęp tylko przez metody)
  • wlasciciel – private z Get/Set
  • Metody: Wplata(kwota), Wyplata(kwota), GetSaldo()
  • Walidacja: nie można wypłacić więcej niż saldo, kwoty muszą być > 0

⭐⭐ Zadanie 5: System konfiguracji

Utwórz klasę Konfiguracja z:

  • Stałymi (const): WERSJA_APLIKACJI, MAX_UZYTKOWNICY
  • Polami static readonly: DataUruchomienia, NazwaKomputera
  • Polami static: TrybDebug (bool), JezykAplikacji (string)
  • Metodami statycznymi do zmiany ustawień z walidacją

💡 Podpowiedź: Environment.MachineName zwraca nazwę komputera