8. Programowanie obiektowe – Klasy, konstruktory, destruktory, static
8. Programowanie obiektowe – Klasy, konstruktory, destruktory, static
Punkty w nagraniu:
- Czym jest klasa?
- Definicja klasy
- Konstruktory
- Destruktor
- Statyczne składniki klasy
- Klasa statyczna
- Przykłady z poziomu kodu
Linki:
- Projekt do pobrania: Link
1. Czym jest klasa?

Klasa jest zbiorem informacji o tym z czego składa się nasz obiekt oraz jakie operacje może wykonywać. Obiektem nazywamy instancję klasy, czyli utworzony obiekt. Metody, zmienne, właściwości są elementami / składowymi klasy.
Dla przykładu mamy drzwi, możemy o nich powiedzieć, że posiada takie informacje jak:
- Waga
- Wysokość
- Szerokość
- Kolor
- Materiał
- Czy otwarte
oraz akcje takie jak:
- Otwórz
- Zamknij
Te wszystkie informacje możemy w prosty sposób zamienić na kod, a dokładniej stworzyć na ich podstawie prostą klasę
public class Door { public float Weight; public float Height; public float Wight; public string Color; public string Material; public bool IsOpen; public void Open() { // Jakiś kod } public void Close() { // Jakiś kod } }
W powyższym przykładzie w prosty sposób utworzyliśmy klasę Door z informacjami oraz metodami, które wymieniliśmy.
2. Definicja klasy

Tworzenie klasy zaczynamy od modyfikatora dostępu (opcjonalnie), odpowiada on za to czy dostęp do klasy będzie możliwy w konkretnym miejscu. Potem piszemy słowo kluczowe class i nazwę naszej klasy, przy tworzeniu nazwy, korzystamy z nazewnictwa Pascal’a, no ale dokładniej o tym poniżej.
- modyfikator dostępu – określa widoczność zmiennej lub metody. Gdy nie podamy żadnego modyfikatora to kompilator uzna to jako private. Więcej o modyfikatorach dostępu omówię w jednym z przyszłych materiałów.
- słowo kluczowe – przy tworzeniu klasy korzystamy ze słowa kluczowego class, które oznacza klasę
- nazwa klasy – przy tworzeniu nazwy klasy korzystamy ze stylu nazewnictwa Pascal’a, czyli zaczynamy z dużej litery i jeśli klasa w nazwie ma więcej niż jedno słowo to każde kolejne słowo zaczynamy z dużej litery. Nazwa klasy nie może zaczynać się od cyfry i znaków specjalnych takich jak ,.;@#
3. Konstruktory
public class Door { public float Weight; public float Height; public Door() { // Jakiś kod } }
Konstruktor jest czymś w stylu metody, tylko dotyczy bezpośrednio klasy i jest wywoływany w trakcie tworzenia obiektu klasy, czyli inicjalizacji. Konstruktor tworzymy od podania modyfikatora dostępu i jego nazwy, nazwą będzie nazwa klasy, jak można łatwo zauważyć, konstruktor nie zwraca żadnego typu.
W przykładzie obok mamy utworzony konstruktor domyślny, czyli taki, który jest wywoływany w trakcie tworzenia obiektu, jeśli nie umieścimy w nim żadnego kodu to nie ma sensu go tworzyć, ponieważ i tak w trakcie tworzenia obiektu, będzie wywołany automatycznie.
Kiedy warto z niego korzystać? w sytuacji kiedy chcielibyśmy na starcie zainicjalizować jakieś zmienne, listy itd.
public class Door { public float Weight; public float Height; public Door(float weight) { Weight = weight; } public Door(float weight, float height) { Weight = weight; Height = height; } }
W tym przykładzie mamy już dwa konstruktory z parametrami, nie mamy za to konstruktora domyślnego. Gdy mamy więcej niż jeden konstruktor, który posiada różną ilość parametrów, nazywamy je konstruktorami przeciążonymi (związane z polimorfizmem, ale o tym w którymś z kolejnych materiałów).
Dzięki takiemu rozwiązaniu możemy utworzyć obiekty klasy Door podając od razu jeden lub dwa parametry.
Niestety nie można utworzyć obiektu klasy Door bez podania parametrów, ponieważ takiego konstruktora nie posiadamy i automatycznie nie będzie on wywołany, ponieważ posiadamy konstruktory przeciążone

Tak jak wspomniałem wyżej, jeśli będziemy chcieli utworzyć obiekt klasy Door w którym mamy przeciążone konstruktory to przy próbie utworzenia obiektu korzystając z konstruktora domyślnego, otrzymamy błąd, który mówi nam o tym, że klasa Door nie posiada konstruktora bez parametrów.
4. Destruktory
public class Door { ~Door() { // Jakiś kod } }
Destruktor pozwala nam zrobić porządki, czyli w nim możemy umieścić kod, który będzie zwalniał zasoby pamięci w momencie, gdy zniknie ostatnia referencja do obiektu. Dodatkowo działa jak Garbage Collector, czyli możesz mieć kontrolę nad tym procesem.
Destruktor tworzymy podobnie jak konstruktor z taką różnicą, że na początku dodajemy ~ i nie podajemy typu dostępu, parametrów i typu zwracanego.
5. Statyczne składniki klasy
public class Door { public static int Counter; public Door() { Counter++; } public static void ShowNumberOfDoors() { Console.WriteLine($"Utworzono {Counter} drzwi."); } }
Element, składnik statyczny w klasie tworzymy w podobny sposób do zwykłych zmiennych, zaczynamy od modyfikatora dostępu, później podajemy słowo kluczowe static i dalsza część bez zmian, czyli typ zmiennej i nazwa.
Tak utworzona np. zmienna jest dostępna w całym projekcie i odwołujemy się do niej po przez odwołanie się do nazwy klasy w której występuje, podając jej nazwę np. w przykładzie obok wyglądało by to tak Door.Counter, gdybyśmy chcieli odwołać się do zmiennej Counter
Tego typu metody, zmienne statyczne mogą być modyfikowane, odczytane w każdym miejscu w projekcie co za tym idzie w jednym miejscu możemy ustawić Counter = 100, w drugim miejscu go odczytać i w trzecim miejscu zmienić jego wartość, wtedy przy kolejnym odczycie w obojętnie jakim miejscu będzie miał ostatnią przypisaną wartość.
List<Door> doors = new List<Door>() { new Door(), new Door(), new Door(), }; Door.ShowNumberOfDoors();

W podanym przykładzie utworzyliśmy listę elementów klasy Door, dodaliśmy trzy obiekty i wywołaliśmy statyczną metodę odpowiedzialną za wyświetlenie liczby drzwi. Jak widać, w trakcie tworzenia obiekty Door, inkrementowało nam zmienną Counter, dzięki czemu wiemy ile drzwi utworzono.
doors.Add(new()); doors.Add(new()); Door.ShowNumberOfDoors();

Dodajemy kolejne dwa obiekty do listy i wyświetlamy liczbę drzwi tak dla pewności, prawidłowo liczba została zwiększona
Door.Counter = 100; Door.ShowNumberOfDoors();

I tutaj mamy przykład tej modyfikacji, mimo, że wcześniej stworzyliśmy 5 drzwi, mamy Counter z wartością 5 to i tak innym miejscu możemy zmienić tą wartość bez największego problemu.. dlatego trzeba dobrze przemyśleć, kiedy chcielibyśmy skorzystać ze zmiennych statycznych i czy na pewno nie dałoby się zrobić tego inaczej.
6. Klasa statyczna
public static class Calculator { public static void Add(int a, int b) => Console.WriteLine($"Dodawanie: {a} + {b} = {a + b}"); public static void Delete(int a, int b) => Console.WriteLine($"Odejmowanie: {a} - {b} = {a - b}"); public static void Multiply(int a, int b) => Console.WriteLine($"Mnożenie: {a} * {b} = {a * b}"); public static void Divide(float a, float b) { if (b <= 0) //throw new DivideByZeroException("Nie można dzielić przez 0"); { Console.WriteLine("Nie można dzielić przez 0"); return; } Console.WriteLine($"Dzielenie: {a} / {b} = {a / b}"); } }
Calculator.Add(5, 6); Calculator.Delete(20, 25); Calculator.Multiply(5, 6); Calculator.Divide(90, 9); Calculator.Divide(10, 0);

Klasę statyczną tworzymy w podobny sposób do normalnej klasy, z taką różnicą, że dodajemy słowo kluczowe static za modyfikatorem dostępu (o ile takowy podajemy).
Jako przykład stworzyłem klasę Calculator w której wykonujemy proste operacje arytmetyczne włącznie z wyświetleniem wyników.
Klasa statyczna musi i w sumie może zawierać tylko elementy statyczne, więc wszelkie zmienne, właściwości, metody muszą być statyczne.
Dodatkowo nie można utworzyć obiektu/instancji klasy statycznej jak i nie można z niej dziedziczyć.

Podsumowując, klasy bardzo ułatwią nam sprawę, gdy będziemy chcieli operować na wielu informacjach na raz, w sumie w C# głównie się na nich operuje, więc dobrze jest za wczasu je poznać. W materiale dość ogólnie powiedziałem o klasach, ponieważ klasy mogą być różnego typu np. może być klasa abstrakcyjna, klasa zapieczętowana no ale o tym pewnie w kolejnych wpisach/materiałach. Mam nadzieję, że materiał wam się przydał:)
