Co to są pola w klasie?
Pola (fields) to zmienne zdefiniowane wewnątrz klasy, które przechowują dane obiektu. Można je porównać do „schowków” – każdy obiekt ma swoje własne kopie tych schowków z własnymi wartościami.
Analogia z życia codziennego
Wyobraź sobie szafkę ucznia w szkole:
- Klasa = projekt szafki
- Obiekt = konkretna szafka ucznia
- Pola = przegródki w szafce (na książki, zeszyty, telefon)
- Modyfikatory dostępu = kto może otwierać które przegródki
public class Uczen
{
// To są pola klasy - "przegródki" każdego ucznia
public string imie; // Każdy może zobaczyć imię
private string numerTelefonu; // Tylko sam uczeń ma dostęp
protected double srednia; // Uczeń i jego "krewni" (klasy pochodne)
}Podstawowe typy pól
Pola instancji (zwykłe)
Każdy obiekt ma swoją własną kopię:
public class Samochod
{
// Każdy samochód ma swoje własne wartości
public string marka;
public string model;
public int rocznik;
public double przebieg;
public bool czySprawny;
}
// Każdy obiekt ma niezależne wartości pól
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!Pola statyczne (wspólne dla wszystkich)
Jedna wartość dzielona między wszystkie obiekty:
public class Licznik
{
public static int iloscObiektow = 0; // Wspólne dla wszystkich!
public string nazwa; // Każdy obiekt ma swoją własną
public Licznik(string nazwa)
{
this.nazwa = nazwa;
iloscObiektow++; // Zwiększamy wspólny licznik
}
}
// Używanie
Licznik obj1 = new Licznik("Pierwszy");
Licznik obj2 = new Licznik("Drugi");
Licznik obj3 = new Licznik("Trzeci");
Console.WriteLine(Licznik.iloscObiektow); // 3 - wspólna wartość!
// Uwaga: dostęp przez nazwę klasy, nie obiektModyfikatory dostępu – „Poziomy bezpieczeństwa”
Modyfikatory dostępu kontrolują, kto może używać pól klasy.
public – „Otwarte dla wszystkich”
public class Osoba
{
public string imie; // Każdy może czytać i zmieniać
public int wiek;
}
class Program
{
static void Main()
{
Osoba osoba = new Osoba();
// Każdy może używać pól public
osoba.imie = "Jan"; // ✅ OK
osoba.wiek = 25; // ✅ OK
Console.WriteLine(osoba.imie); // ✅ OK
Console.WriteLine(osoba.wiek); // ✅ OK
}
}private – „Tylko dla mnie”
public class KontoBankowe
{
public string numerKonta; // Wszyscy mogą zobaczyć numer
private double saldo; // Tylko klasa może zmieniać saldo
private string pin; // Nikt z zewnątrz nie ma dostępu
public KontoBankowe(string numer, double poczatkoweSaldo)
{
numerKonta = numer;
saldo = poczatkoweSaldo; // ? OK - jesteśmy w klasie
pin = "0000";
}
public void WyswietlSaldo()
{
Console.WriteLine($"Saldo: {saldo} zł"); // ? OK - jesteśmy w klasie
}
public void Wplata(double kwota)
{
if (kwota > 0)
{
saldo += kwota; // ? OK - metoda w klasie może zmieniać private
}
}
}
class Program
{
static void Main()
{
KontoBankowe konto = new KontoBankowe("123456", 1000);
konto.numerKonta = "654321"; // ? OK - public
// konto.saldo = 5000; // ? BŁĄD - private!
// Console.WriteLine(konto.pin); // ? BŁĄD - private!
konto.WyswietlSaldo(); // ? OK - public metoda
konto.Wplata(500); // ? OK - public metoda
}
}protected – „Dla rodziny”
public class Zwierze
{
public string gatunek; // Wszyscy widzą
protected string dnaKod; // Tylko zwierzę i jego "dzieci"
private string tajnyKod; // Tylko to zwierzę
public Zwierze(string gatunek)
{
this.gatunek = gatunek;
this.dnaKod = "ACGT-1234"; // ? OK - jesteśmy w klasie
this.tajnyKod = "SECRET"; // ? OK - jesteśmy w klasie
}
}
public class Pies : Zwierze // Pies dziedziczy po Zwierze
{
public string rasa;
public Pies(string rasa) : base("Canis lupus")
{
this.rasa = rasa;
// Dostęp do pól z klasy bazowej:
Console.WriteLine(gatunek); // ? OK - public
Console.WriteLine(dnaKod); // ? OK - protected (jesteśmy "dzieckiem")
// Console.WriteLine(tajnyKod); // ? BŁĄD - private (nawet dla dzieci!)
}
}
class Program
{
static void Main()
{
Pies pies = new Pies("Labrador");
Console.WriteLine(pies.gatunek); // ? OK - public
// Console.WriteLine(pies.dnaKod); // ? BŁĄD - protected (nie jesteśmy "rodziną")
Console.WriteLine(pies.rasa); // ? OK - public
}
}internal – „Dla tego projektu”
public class UstawieniaAplikacji
{
public string nazwaAplikacji; // Wszyscy widzą
internal string sciezkaKonfiguracji; // Tylko klasy w tym samym projekcie
private string hasloAdmina; // Tylko ta klasa
}
// W tym samym projekcie:
class InnaKlasa
{
public void TestDostep()
{
UstawieniaAplikacji ustawienia = new UstawieniaAplikacji();
ustawienia.nazwaAplikacji = "MojaApp"; // ✅ OK - public
ustawienia.sciezkaKonfiguracji = "C:\\config"; // ✅ OK - internal (ten sam projekt)
// ustawienia.hasloAdmina = "admin123"; // ❌ BŁĄD - private
}
}Wizualizacja modyfikatorów dostępu
┌─────────────────────────────────────────────────────────┐
│ WSZĘDZIE │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ PROJEKT │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ HIERARCHIA KLAS │ │ │
│ │ │ ┌─────────────────────────────────────────────┐ │ │ │
│ │ │ │ TA KLASA │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ private - tylko tutaj │ │ │ │
│ │ │ └─────────────────────────────────────────────┘ │ │ │
│ │ │ protected - tutaj + klasy pochodne │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ internal - tutaj + inne klasy w projekcie │ │
│ └─────────────────────────────────────────────────────┘ │
│ public - tutaj + inne projekty + wszędzie │
└─────────────────────────────────────────────────────────┘Pola tylko do odczytu (readonly)
Pola readonly można ustawić tylko w konstruktorze:
public class Osoba
{
public readonly string pesel; // Można ustawić tylko raz
public readonly DateTime dataUrodzenia; // Nie zmieni się po utworzeniu
public string imie; // Można zmieniać w dowolnym momencie
public Osoba(string pesel, DateTime dataUrodzenia, string imie)
{
this.pesel = pesel; // ✅ OK - jesteśmy w konstruktorze
this.dataUrodzenia = dataUrodzenia; // ✅ OK
this.imie = imie;
}
public void ZmienDane(string noweImie)
{
imie = noweImie; // ✅ OK - zwykłe pole
// pesel = "98765432101"; // ❌ BŁĄD - readonly można ustawić tylko w konstruktorze!
}
}Pola stałe (const)
Wartości const są ustalane w czasie kompilacji i nie mogą się zmienić:
public class Matematyka
{
public const double PI = 3.14159265359; // Stała matematyczna
public const int SEKUNDY_W_MINUCIE = 60; // Nigdy się nie zmieni
public const string WERSJA_APLIKACJI = "1.0"; // Stała wersja
public static readonly DateTime DataKompilacji = DateTime.Now; // Ustawiane przy uruchomieniu
public void PrzykladUzycia()
{
double obwod = 2 * PI * 10; // Używanie stałej
Console.WriteLine($"Obwód: {obwod}");
// PI = 3.14; // ❌ BŁĄD - nie można zmieniać const!
}
}
// Używanie stałych
double pole = Matematyka.PI * 5 * 5; // Dostęp przez nazwę klasyRóżnica: const vs readonly vs static readonly
public class PorownaniePol
{
// const - wartość ustalona w kodzie, nie można zmienić
public const string CONST_POLE = "Nigdy się nie zmieni";
// readonly - można ustawić w konstruktorze, potem niezmienne
public readonly string readonlyPole;
// static readonly - wspólne dla wszystkich obiektów, ustawiane raz
public static readonly DateTime StaticReadonlyPole = DateTime.Now;
public PorownaniePol(string wartosc)
{
readonlyPole = wartosc; // ✅ OK - można ustawić w konstruktorze
// CONST_POLE = "nowa wartość"; // ❌ BŁĄD!
}
}Praktyczne przykłady
Przykład 1: Klasa Student z różnymi poziomami dostępu
public class Student
{
// Dane publiczne - każdy może zobaczyć
public string imie;
public string nazwisko;
public string kierunek;
// Dane chronione - tylko dla systemu szkoły
internal string numerIndeksu;
internal int rokStudiow;
// Dane prywatne - tylko dla obiektu studenta
private double sredniaOcen;
private string hasloDoSystemu;
// Stałe - nie zmieniają się
public const int MIN_PUNKTY_ZALICZENIE = 50;
public const int MAX_PUNKTY = 100;
public Student(string imie, string nazwisko, string kierunek, string nrIndeksu)
{
this.imie = imie;
this.nazwisko = nazwisko;
this.kierunek = kierunek;
this.numerIndeksu = nrIndeksu;
this.rokStudiow = 1;
this.sredniaOcen = 0.0;
this.hasloDoSystemu = GenerujHaslo();
}
// Publiczne metody do dostępu do prywatnych danych
public double PobierzSrednia()
{
return sredniaOcen;
}
public void DodajOcene(double ocena)
{
if (ocena >= 2.0 && ocena <= 5.0)
{
// Logika obliczania nowej średniej
sredniaOcen = (sredniaOcen + ocena) / 2;
}
}
public bool CzyZaliczyl()
{
return sredniaOcen * 20 >= MIN_PUNKTY_ZALICZENIE; // Przeliczenie na punkty
}
private string GenerujHaslo()
{
return $"{imie.ToLower()}{numerIndeksu.Substring(0, 3)}";
}
}Przykład 2: Klasa z polami statycznymi
public class LicznikObiektow
{
// Pola statyczne - wspólne dla wszystkich obiektów
public static int lacznie = 0;
private static int nastepnyId = 1;
// Pola instancji - każdy obiekt ma swoje
public readonly int id;
public string nazwa;
public LicznikObiektow(string nazwa)
{
this.id = nastepnyId++; // Każdy obiekt dostaje unikalny ID
this.nazwa = nazwa;
lacznie++; // Zwiększamy licznik wszystkich obiektów
}
public static void WyswietlStatystyki()
{
Console.WriteLine($"Utworzonych obiektów: {lacznie}");
Console.WriteLine($"Następny ID: {nastepnyId}");
}
}
// Używanie
LicznikObiektow obj1 = new LicznikObiektow("Pierwszy");
LicznikObiektow obj2 = new LicznikObiektow("Drugi");
LicznikObiektow obj3 = new LicznikObiektow("Trzeci");
Console.WriteLine(obj1.id); // 1
Console.WriteLine(obj2.id); // 2
Console.WriteLine(obj3.id); // 3
LicznikObiektow.WyswietlStatistyki(); // "Utworzonych obiektów: 3, Następny ID: 4"Najczęstsze błędy i dobre praktyki
Błąd 1: Wszystko public
// ŹLE - wszystko publiczne!
public class ZleKonto
{
public double saldo; // Każdy może zmienić saldo!
public string pin; // PIN widoczny dla wszystkich!
public bool czyAktywne;
}
// DOBRZE - kontrolowany dostęp
public class DobreKonto
{
private double saldo; // Chronione saldo
private string pin; // Chroniony PIN
public bool czyAktywne; // OK - może być publiczne
// Metody do bezpiecznego dostępu
public double PobierzSaldo() { return saldo; }
public bool Wyplata(double kwota, string podanyPin)
{
if (podanyPin == pin && kwota <= saldo)
{
saldo -= kwota;
return true;
}
return false;
}
}Błąd 2: Zapomnienie o inicjalizacji
public class Osoba
{
public string imie; // Domyślnie null - może powodować błędy!
public int wiek; // Domyślnie 0 - OK
public bool aktywny; // Domyślnie false - OK
// LEPIEJ - z inicjalizacją
public string imie2 = ""; // Puste, ale nie null
public List<string> hobby = new List<string>(); // Pusta lista, ale nie null
}Dobre praktyki
public class DobrePraktyki
{
// 1. Używaj znaczących nazw
public string nazwaUzytkownika; // ✅ DOBRZE
// public string n; // ❌ ŹLE
// 2. Prywatne pola z podkreślnikiem (konwencja)
private string _haslo; // ✅ Popularna konwencja
private int _licznikProb;
// 3. Stałe wielkimi literami
public const double STAWKA_VAT = 0.23; // ✅ DOBRZE
// public const double vat = 0.23; // ❌ ŹLE
// 4. Readonly dla danych, które się nie zmieniają
public readonly DateTime dataRejestracji = DateTime.Now;
// 5. Protected tylko gdy planujemy dziedziczenie
protected string bazoweDane; // ✅ Jeśli klasa będzie bazowa
}Kluczowe wnioski
- Pola przechowują stan obiektu – dane obiektu
- public – dostęp z wszędzie (używać ostrożnie!)
- private – dostęp tylko z tej klasy (najbezpieczniejsze)
- protected – dostęp z tej klasy + klasy pochodne
- internal – dostęp z tego projektu
- static – pole wspólne dla wszystkich obiektów klasy
- readonly – można ustawić tylko w konstruktorze
- const – wartość stała ustalona w kodzie
- Encapsulation – ukrywanie implementacji, udostępnianie interfejsu
Do zapamiętania
„Pola = szuflady obiektu” „private domyślnie, public tylko gdy naprawdę trzeba” „const = nigdy się nie zmieni, readonly = można ustawić raz” „static = jeden dla wszystkich, zwykłe = każdy ma swoje”
Zadania do przećwiczenia
Zadanie 1: Klasa z różnymi modyfikatorami
Napisz klasę Pracownik z polami:
- imie (publiczne)
- nazwisko (publiczne)
- pensja (prywatne)
- stanowisko (internal)
- dataZatrudnienia (readonly)
- MINIMALNA_PENSJA (const = 3000)
Dodaj konstruktor i metody do bezpiecznego dostępu do pensji.
Zadanie 2: Pola statyczne
Napisz klasę Produkt z polami statycznymi:
iloscWszystkichProduktow– ile produktów utworzononastepnyKod– kolejny numer produktu
Każdy nowy produkt powinien dostać unikalny kod.
Zadanie 3: Znajdź błędy
public class TestKlasa
{
public const string nazwa; // Błąd 1
private readonly int liczba; // Błąd 2
public static wiek = 25; // Błąd 3
protected string dane = "test";
public void TestMetoda()
{
nazwa = "Nowa nazwa"; // Błąd 4
liczba = 100; // Błąd 5
}
}Zadanie 4: Refaktoryzacja
Popraw tę klasę stosując dobre praktyki:
public class zleklasa
{
public string n;
public string s;
public double p;
public bool a;
public string haslo;
public double saldo;
}