Static w C# – współdzielone zasoby klasy
Poznasz słowo kluczowe static – pola i metody należące do klasy, nie do obiektów. Nauczysz się tworzyć liczniki instancji, klasy pomocnicze i zrozumiesz działanie Math, Console czy File!
Problem – dane wspólne dla wszystkich obiektów
Czasami potrzebujesz danych lub funkcji wspólnych dla wszystkich obiektów danej klasy. Jak to zrobić?
// 😰 PROBLEM: Chcemy zliczać ile obiektów utworzono public class Uzytkownik { public string Nazwa; public int Licznik; // Każdy obiekt ma SWÓJ licznik! public Uzytkownik(string nazwa) { Nazwa = nazwa; Licznik++; // Zwiększa tylko SWÓJ licznik } } Uzytkownik u1 = new Uzytkownik("Jan"); Uzytkownik u2 = new Uzytkownik("Anna"); Uzytkownik u3 = new Uzytkownik("Piotr"); Console.WriteLine(u1.Licznik); // 1 – każdy ma swój! Console.WriteLine(u2.Licznik); // 1 Console.WriteLine(u3.Licznik); // 1 // Chcieliśmy: 3 (łączna liczba użytkowników)
Zwykłe pola należą do każdego obiektu osobno. Potrzebujemy czegoś, co należy do klasy, nie do obiektów!
Czym jest static?
Słowo kluczowe static oznacza, że element należy do klasy, nie do obiektów. Istnieje jedna kopia dla całej klasy.
Wyobraź sobie klasę szkolną:
- Pola instancyjne = zeszyt każdego ucznia (każdy ma swój)
- Pola statyczne = tablica ogłoszeń (jedna dla wszystkich)
Gdy nauczyciel napisze ogłoszenie, wszyscy je widzą!
- Metoda instancyjna = pisanie w swoim zeszycie (potrzebujesz SWOJEGO zeszytu)
- Metoda statyczna = kalkulator na biurku nauczyciela (jeden dla wszystkich)
Pola statyczne
Pole statyczne to zmienna należąca do klasy. Istnieje tylko jedna kopia.
public class Uzytkownik { // Pole STATYCZNE – jedno dla całej klasy public static int LiczbaUzytkownikow = 0; // Pole INSTANCYJNE – osobne dla każdego obiektu public string Nazwa; public Uzytkownik(string nazwa) { Nazwa = nazwa; Uzytkownik.LiczbaUzytkownikow++; // Wspólny licznik } } // Przed utworzeniem Console.WriteLine(Uzytkownik.LiczbaUzytkownikow); // 0 Uzytkownik u1 = new Uzytkownik("Jan"); Uzytkownik u2 = new Uzytkownik("Anna"); Uzytkownik u3 = new Uzytkownik("Piotr"); Console.WriteLine(Uzytkownik.LiczbaUzytkownikow); // 3 ✅
Do pól statycznych odwołujesz się przez nazwę klasy:
Uzytkownik.LiczbaUzytkownikow // ✅ Przez klasę u1.LiczbaUzytkownikow // ⚠️ Działa, ale ZŁA PRAKTYKA
Stałe statyczne
public class Konfiguracja { public const double PI = 3.14159265359; public static readonly string Wersja = "1.0.0"; } // Użycie – bez new! Console.WriteLine(Konfiguracja.PI); // 3.14159... Console.WriteLine(Konfiguracja.Wersja); // 1.0.0
Pola statyczne vs instancyjne
Pole instancyjne
- Należy do: obiektu
- Kopie: każdy obiekt ma swoją
- Dostęp:
obiekt.Pole - Przykład: Imię użytkownika
Pole statyczne
- Należy do: klasy
- Kopie: jedna dla klasy
- Dostęp:
Klasa.Pole - Przykład: Liczba użytkowników
| Aspekt | Instancyjne | Statyczne |
|---|---|---|
| Słowo kluczowe | brak | static |
| Liczba kopii | N (ile obiektów) | 1 |
| Wymaga obiektu? | Tak | Nie |
| Żyje | Z obiektem | Cały program |
Metody statyczne
Metoda statyczna należy do klasy. Możesz ją wywołać bez tworzenia obiektu.
public class Kalkulator { public static int Dodaj(int a, int b) { return a + b; } public static double Pierwiastek(double x) { return Math.Sqrt(x); } } // Użycie – BEZ tworzenia obiektu! int suma = Kalkulator.Dodaj(5, 3); // 8 double wynik = Kalkulator.Pierwiastek(16); // 4.0 // NIE musisz robić: // Kalkulator k = new Kalkulator(); // k.Dodaj(5, 3);
Metoda statyczna nie ma dostępu do pól i metod instancyjnych!
public class Osoba { public string Imie; // Instancyjne public static int LiczbaOsob = 0; // Statyczne public static void WyswietlLiczbe() { Console.WriteLine(Osoba.LiczbaOsob); // ✅ OK // Console.WriteLine(Imie); // ❌ BŁĄD! } }
| Może używać? | Metoda instancyjna | Metoda statyczna |
|---|---|---|
| Pola instancyjne | ✅ Tak | ❌ Nie |
| Pola statyczne | ✅ Tak | ✅ Tak |
| Metody instancyjne | ✅ Tak | ❌ Nie |
| Metody statyczne | ✅ Tak | ✅ Tak |
Kiedy używać static?
| ✅ Użyj static gdy… | Przykład |
|---|---|
| Nie potrzebujesz danych obiektu | Math.Sqrt() |
| To funkcja pomocnicza (utility) | StringHelper.Trim() |
| Metoda fabrykująca | DateTime.Parse() |
| Operacje na danych wspólnych | Licznik, konfiguracja |
| Punkt wejścia programu | Main() |
| ❌ NIE używaj static gdy… | Dlaczego? |
|---|---|
| Metoda operuje na danych obiektu | Potrzebujesz this |
| Zachowanie zależy od stanu | Każdy obiekt może działać inaczej |
| Metoda może być nadpisana | Static nie wspiera polimorfizmu |
Klasy statyczne
Klasa statyczna zawiera tylko elementy statyczne. Nie można tworzyć jej instancji.
public static class Walidator { public static bool CzyEmail(string tekst) { return tekst.Contains("@") && tekst.Contains("."); } public static bool CzyNumerTelefonu(string tekst) { return tekst.Length == 9 && tekst.All(char.IsDigit); } } // Użycie – bez new! bool ok = Walidator.CzyEmail("jan@example.com"); // true // ❌ NIE MOŻNA utworzyć instancji! // Walidator w = new Walidator(); // BŁĄD!
staticprzedclass- Wszystkie składowe MUSZĄ być statyczne
- Nie można utworzyć obiektu
- Nie może dziedziczyć
- Idealna na funkcje pomocnicze
Konstruktor statyczny
Konstruktor statyczny jest wywoływany automatycznie, jeden raz, przy pierwszym użyciu klasy.
public class Baza { public static string ConnectionString; public static DateTime DataUruchomienia; // Bez public! Bez parametrów! static Baza() { Console.WriteLine("Inicjalizacja..."); ConnectionString = "Server=localhost"; DataUruchomienia = DateTime.Now; } } Console.WriteLine("Start"); Console.WriteLine(Baza.ConnectionString); // Tu się odpala! Console.WriteLine(Baza.ConnectionString); // Już nie // Wynik: // Start // Inicjalizacja... // Server=localhost // Server=localhost
- Brak modyfikatora dostępu
- Brak parametrów
- Wywoływany automatycznie
- Tylko raz
Właściwości statyczne
public class Aplikacja { private static int _licznik = 0; public static int Licznik { get { return _licznik; } private set { _licznik = value; } } public static string Nazwa { get; set; } = "MojaApka"; public static DateTime Start { get; } = DateTime.Now; } Console.WriteLine(Aplikacja.Nazwa); // MojaApka Console.WriteLine(Aplikacja.Start); // 2024-01-15...
Przykład: DateTime.Now
// DateTime.Now to właściwość statyczna! DateTime teraz = DateTime.Now; DateTime dzisiaj = DateTime.Today; // Nie tworzysz obiektu – to moc static!
Static w .NET
| Klasa | Typ | Przykłady |
|---|---|---|
Math | Klasa statyczna | Math.Sqrt(), Math.PI |
Console | Klasa statyczna | Console.WriteLine() |
File | Klasa statyczna | File.ReadAllText() |
Environment | Klasa statyczna | Environment.UserName |
DateTime | Struct z static | DateTime.Now, DateTime.Parse() |
int | Struct z static | int.Parse(), int.MaxValue |
string | Klasa z static | string.IsNullOrEmpty() |
// Math double x = Math.Sqrt(25); // 5 double y = Math.Pow(2, 10); // 1024 int z = Math.Max(5, 10); // 10 // int int n = int.Parse("42"); // 42 int max = int.MaxValue; // 2147483647 // string bool ok = string.IsNullOrEmpty(""); // true string s = string.Join(", ", new[] {"a", "b"}); // "a, b" // File bool istnieje = File.Exists("plik.txt"); string tekst = File.ReadAllText("dane.txt");
Wzorzec: Licznik instancji
public class Produkt { private static int _licznik = 0; public static int LiczbaProduktow => _licznik; public int Id { get; } public string Nazwa { get; set; } public decimal Cena { get; set; } public Produkt(string nazwa, decimal cena) { _licznik++; Id = _licznik; // Unikalne ID Nazwa = nazwa; Cena = cena; } } Produkt p1 = new Produkt("Laptop", 2500); Produkt p2 = new Produkt("Myszka", 50); Console.WriteLine(p1.Id); // 1 Console.WriteLine(p2.Id); // 2 Console.WriteLine(Produkt.LiczbaProduktow); // 2
Wzorzec: Klasa pomocnicza (utility)
public static class StringHelper { public static string Capitalize(string s) { if (string.IsNullOrEmpty(s)) return s; return char.ToUpper(s[0]) + s.Substring(1).ToLower(); } public static string Reverse(string s) { char[] arr = s.ToCharArray(); Array.Reverse(arr); return new string(arr); } public static bool IsPalindrome(string s) { string clean = s.ToLower().Replace(" ", ""); return clean == Reverse(clean); } public static int CountWords(string s) { if (string.IsNullOrWhiteSpace(s)) return 0; return s.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length; } } // Użycie string x = StringHelper.Capitalize("hELLO"); // "Hello" string y = StringHelper.Reverse("ABC"); // "CBA" bool p = StringHelper.IsPalindrome("kajak"); // true int w = StringHelper.CountWords("To jest"); // 2
Częste błędy
❌ Błąd 1: Dostęp do instancyjnego z static
❌ Źle
public class Osoba
{
public string Imie;
public static void Wyswietl()
{
Console.WriteLine(Imie);
// BŁĄD! Static nie zna Imie
}
}
✅ Dobrze
public class Osoba
{
public string Imie;
public void Wyswietl() // Bez static!
{
Console.WriteLine(Imie);
}
}
❌ Błąd 2: Dostęp przez obiekt zamiast klasę
⚠️ Działa, ale ZŁE
Osoba o = new Osoba(); Console.WriteLine(o.LiczbaOsob); // Działa, ale myląca składnia
✅ Dobrze
Console.WriteLine(Osoba.LiczbaOsob); // Jasno widać że to static!
❌ Błąd 3: Tworzenie instancji klasy statycznej
❌ Źle
public static class Helper { }
Helper h = new Helper();
// BŁĄD KOMPILACJI!
✅ Dobrze
public static class Helper
{
public static void Metoda() { }
}
Helper.Metoda(); // Bez new!
❌ Błąd 4: public w konstruktorze statycznym
❌ Źle
public class X
{
public static X() // BŁĄD!
{
}
}
✅ Dobrze
public class X
{
static X() // Bez modyfikatora!
{
}
}
Podsumowanie
- static – element należy do klasy, nie do obiektów
- Pole statyczne – jedna kopia dla całej klasy
- Metoda statyczna – wywoływana bez obiektu
- Klasa statyczna – tylko elementy static, bez new
- Konstruktor statyczny – raz, automatycznie
| Element | Składnia | Dostęp |
|---|---|---|
| Pole statyczne | public static int X; | Klasa.X |
| Metoda statyczna | public static void F() | Klasa.F() |
| Klasa statyczna | public static class K | K.Metoda() |
| Konstruktor stat. | static Klasa() | automatycznie |
Schemat składni
// Klasa z elementami static i instancyjnymi public class Przyklad { public static int Licznik; // Statyczne public string Nazwa; // Instancyjne static Przyklad() { } // Konstr. statyczny public Przyklad() { } // Konstr. instancyjny public static void MetodaStat() { } // Statyczna public void MetodaInst() { } // Instancyjna } // Klasa w pełni statyczna public static class Helper { public static void Metoda() { } }
Zadania praktyczne
📝 Zadanie 1: Licznik Studentów
Utwórz klasę Student z:
- Polami: Imie, NumerIndeksu
- Polem statycznym
LiczbaStudentow - Konstruktorem zwiększającym licznik
Utwórz 5 studentów i wyświetl ich liczbę.
📝 Zadanie 2: Generator ID
Utwórz klasę Zamowienie gdzie każde zamówienie ma unikalne ID generowane automatycznie (1, 2, 3…).
💡 Podpowiedź: Użyj statycznego licznika w konstruktorze
📝 Zadanie 3: Klasa MathHelper
Utwórz statyczną klasę MathHelper z metodami:
IsEven(int n)– czy parzystaIsPrime(int n)– czy pierwszaFactorial(int n)– silnia
⭐ Zadanie 4: Konfiguracja aplikacji
Utwórz klasę AppConfig z:
- Statycznymi właściwościami: AppName, Version, MaxUsers
- Statycznym konstruktorem wczytującym wartości
- Metodą
DisplayConfig()
⭐⭐ Zadanie 5: Logger
Utwórz statyczną klasę Logger do logowania:
Log(string message)– wyświetla z datąLogError(string message)– z „ERROR:”LogWarning(string message)– z „WARNING:”- Statyczny licznik
LogCount
💡 Użyj DateTime.Now do formatowania