Rozszerzenie wiedzy o metodach, wprowadzenie indeksatorów oraz mechanizmów hierarchii klas i polimorfizmu.
Student demonstruje umiejętność implementacji dziedziczenia oraz polimorfizmu poprzez tworzenie hierarchii klas z wykorzystaniem słów kluczowych virtual, override oraz base. Zadanie ma na celu pokazanie praktycznego zastosowania indeksatorów do przeszukiwania zbiorów obiektów oraz wykorzystania modyfikatorów out i params w metodach zarządzających zasobami firmy.
Zostałeś poproszony o przygotowanie szkieletu systemu do ewidencji sprzętu komputerowego w dużej korporacji. System musi bazować na ogólnej klasie opisującej zasób (np. model, numer seryjny), z której będą dziedziczyły klasy szczegółowe, takie jak Komputer i Serwer. Każdy typ zasobu posiada własny sposób generowania opisu technicznego, co wymaga zastosowania metod wirtualnych i ich nadpisywania w klasach pochodnych. Dodatkowo musisz stworzyć klasę KontrolerZasobow, która będzie pełniła rolę kontenera na obiekty i udostępni wygodny dostęp do nich za pomocą indeksatora (wyszukiwanie po numerze seryjnym lub indeksie). System powinien również oferować metodę statyczną do szybkiego liczenia statystyk, wykorzystującą parametr out do zwrócenia wielu wyników naraz (np. liczbę serwerów i stacji roboczych). Kolejnym wymaganiem jest funkcja do masowego dodawania tagów opisowych do konkretnego zasobu, w której wykorzystasz parametr params. Program w konsoli powinien utworzyć kilka zróżnicowanych obiektów, dodać je do kontrolera, a następnie przeprowadzić serię testów: wyszukanie zasobu, zmianę jego statusu oraz wyświetlenie polimorficznego raportu. Całość rozwiązania musi być odporna na typowe błędy i stanowić modelowy przykład zastosowania czystego kodu obiektowego w C#.
using System; class Zasob { public string Model { get; set; } public string SN { get; set; } public Zasob(string model, string sn) { Model = model; SN = sn; } public virtual void PokazInfo() => Console.WriteLine($"Zasób: {Model}, SN: {SN}"); } class Komputer : Zasob { public string Procesor { get; set; } // base wywołuje konstruktor rodzica public Komputer(string model, string sn, string cpu) : base(model, sn) { Procesor = cpu; } public override void PokazInfo() => Console.WriteLine($"PC: {Model}, CPU: {Procesor}"); } class Magazyn { private Zasob[] dane = new Zasob[5]; private int licznik = 0; public void Dodaj(Zasob z) { if (licznik < 5) dane[licznik++] = z; } // Indeksator public Zasob this[int i] => dane[i]; // Metoda z params public static void LogujZdarzenie(string opis, params string[] tagi) { Console.WriteLine($"Zdarzenie: {opis}. Tagi: {string.Join(", ", tagi)}"); } // Metoda z out public void GetInfo(out int ilosc, out string pierwszyModel) { ilosc = licznik; pierwszyModel = dane[0]?.Model ?? "Brak"; } } class Program { static void Main() { Magazyn m = new Magazyn(); m.Dodaj(new Komputer("Dell OptiPlex", "SN001", "i7")); m.Dodaj(new Zasob("Monitor LG", "SN002")); // Przykład polimorfizmu for(int i=0; i < 2; i++) m[i].PokazInfo(); Magazyn.LogujZdarzenie("Inwentaryzacja", "KRK", "BUDYNEK1", "PILNE"); int count; string s; m.GetInfo(out count, out s); Console.WriteLine($"Stan: {count}, Pierwszy w bazie: {s}"); } }
Zapoznanie studenta z nietypowymi sposobami przekazywania argumentów do metod w C#. Celem zadania jest opanowanie zwracania wielu wyników jednocześnie oraz projektowanie metod akceptujących dowolną liczbę parametrów wejściowych.
Zlecono Ci przygotowanie zestawu metod pomocniczych dla silnika obliczeniowego, który będzie wykorzystywany przez programistów w innym dziale. Pierwsza metoda ma za zadanie podzielić dwie liczby stałoprzecinkowe, ale musi jednocześnie zwrócić iloraz oraz resztę z dzielenia przy użyciu modyfikatora out. Takie podejście pozwala na uniknięcie pisania dwóch oddzielnych metod dla jednej operacji matematycznej. Kolejnym Twoim zadaniem jest stworzenie uniwersalnego sumatora, który przyjmuje dowolną liczbę liczb całkowitych (typ int) jako parametry, wykorzystując słowo kluczowe params. Dzięki temu użytkownik silnika będzie mógł wywołać metodę przekazując jej dwie, pięć lub nawet zero liczb bez konieczności jawnego tworzenia tablicy. Musisz zadbać o poprawną obsługę błędów, na przykład próbę dzielenia przez zero w pierwszej metodzie. Program w konsoli powinien zaprezentować działanie obu metod dla różnych przypadków testowych. Finalne rozwiązanie ma być proste, szybkie i zgodne ze standardami projektowania metod w środowisku .NET.
Zrozumienie mechanizmu indeksowania obiektów jako sposobu na imitowanie zachowania tablicy wewnątrz własnej klasy. Student uczy się pisać akcesory get i set dla indeksatorów oraz zarządzać bezpiecznym dostępem do kolekcji wewnętrznej.
Zarządzasz bazą danych części zamiennych w warsztacie samochodowym. Zamiast operować na surowej tablicy stringów, chcesz stworzyć klasę "Magazyn", która w sposób inteligentny zarządza zapasami. Twoim zadaniem jest dodanie do tej klasy indeksatora, który pozwoli na pobranie lub zmianę nazwy części za pomocą czytelnej składni: magazyn[3] = "Świeca zapłonowa". Indeksator powinien weryfikować, czy podany indeks mieści się w dopuszczalnym zakresie, i informować o błędzie, jeśli użytkownik spróbuje wyjść poza granice zadeklarowanej pamięci. Możesz również spróbować zaimplementować drugi, przeciążony indeksator, który pozwala wyszukać numer półki na podstawie nazwy części (indeksowanie stringiem). Program w konsoli powinien umożliwić użytkownikowi wypełnienie magazynu kilkoma pozycjami, a następnie ich szybką modyfikację poprzez wspomniany indeksator. To podejście znacznie upraszcza czytanie kodu i sprawia, że Twoje obiekty zachowują się jak natywne kolekcje języka C#. Finalnie system powinien wyświetlić listę wszystkich dostępnych części sformatowaną w kolumnach z odpowiadającymi im numerami indeksów.
Opanowanie mechanizmu dziedziczenia oraz poprawnego wywoływania konstruktorów bazowych za pomocą base. Student uczy się hierarchicznego modelowania danych oraz reużywania kodu z klas nadrzędnych.
Tworzysz system sterowania dla nowoczesnego garażu wielopoziomowego. Musisz przygotować strukturę klas opisującą różne typy pojazdów, zaczynając od ogólnej klasy "Pojazd", która posiada markę oraz typ napędu. Następnie stwórz klasę pochodną "Samochod", która dziedziczy wszystkie te cechy, ale dodaje od siebie unikalne pole, takie jak liczba drzwi czy pojemność bagażnika. Ważnym elementem jest to, aby konstruktor samochodu nie powielał logiki przypisywania marki, lecz przekazywał te dane „do góry” – do klasy nadrzędnej przy użyciu słowa base. Dzięki temu zapewnisz, że każde auto jest przede wszystkim poprawnym obiektem typu pojazd. Twoim zadaniem jest również nadpisanie podstawowej metody wyświetlającej informacje o obiekcie tak, aby samochód prezentował dodatkowe, specyficzne dla niego parametry. Program w konsoli musi utworzyć egzemplarz ogólnego pojazdu oraz egzemplarz konkretnego samochodu i porównać ich zachowanie. Takie podejście pozwala na łatwe dodawanie w przyszłości nowych typów transportu, np. motocykli czy ciężarówek, bez modyfikacji istniejącego silnika systemu. Finalnie wyświetl zestawienie obu obiektów, zwracając uwagę na różnice w ich opisach.
Praktyczne zastosowanie polimorfizmu do rozwiązywania problemów biznesowych. Student uczy się, jak jedna metoda wirtualna zadeklarowana w klasie bazowej może zachowywać się w różny sposób w zależności od rzeczywistego typu obiektu.
W dziale księgowości dużej firmy system naliczania premii rocznych działa według różnych reguł dla różnych stanowisk. Każdy pracownik jest reprezentowany przez klasę "Pracownik", która posiada metodę wirtualną o nazwie ObliczPremie. Dla przeciętnego pracownika biurowego premia wynosi stałe 10% wynagrodzenia bazowego. Musisz jednak stworzyć dwie klasy pochodne: "Dyrektor" oraz "Sprzedawca". Dla dyrektora premia powinna być powiększona o dodatkowy bonus stały za wyniki pionu, natomiast dla sprzedawcy jest ona uzależniona od obrotu, jaki wygenerował w ciągu roku. Twoim zadaniem jest nadpisanie metody ObliczPremie w tych klasach w taki sposób, aby każdy obiekt „wiedział”, jak policzyć własną premię. Kluczowym elementem zadania jest utworzenie tablicy typu Pracownik, w której umieścisz obiekty różnych klas (dyrektora i biuralistę obok siebie), a następnie wywołanie metody obliczeniowej w jednej wspólnej pętli. System powinien automatycznie dopasować algorytm do typu osoby bez konieczności sprawdzania tego w kodzie za pomocą instrukcji if. To zadanie demonstruje potęgę polimorfizmu w zarządzaniu złożonymi procesami biznesowymi.
Zrozumienie subtelnej, ale kluczowej różnicy między nadpisywaniem metod (override) a ich przesłanianiem (new). Student uczy się świadomego zarządzania nazewnictwem metod w hierarchii oraz dowiaduje się, jak wybór słowa kluczowego wpływa na wywołania polimorficzne.
Wyobraź sobie, że piszesz system diagnostyczny dla dwóch typów urządzeń: standardowych sensorów oraz nowoczesnych czujników laserowych. Klasa bazowa "Sensor" posiada metodę Testuj(), która jest wirtualna. Klasa pochodna "SensorKasy" nadpisuje tę metodę (override), zmieniając jej zachowanie na bardziej precyzyjne. Z kolei inna klasa, "SensorSpecjalny", posiada metodę Testuj() oznaczoną słowem kluczowym new, co oznacza, że świadomie przesłania ona wersję z klasy bazowej, nie biorąc udziału w mechanizmie polimorfizmu. Twoim celem jest badanie, co się stanie, gdy przypiszemy oba te obiekty do zmiennych typu bazowego Sensor i wywołamy na nich metodę diagnostyczną. Czy program uruchomi nową wersję testu, czy powróci do starej, bazowej definicji? To zadanie jest bardzo techniczne i ma na celu uniknięcie błędów w dużych frameworkach, gdzie przesłonięcie metody zamiast jej nadpisania może prowadzić do bardzo trudnych do wykrycia błędów logicznych. Program w konsoli powinien wyraźnie pokazać różnice w wywołaniach dla obu przypadków. Dzięki temu zrozumiesz, że słowo kluczowe w sygnaturze metody ma decydujący wpływ na „ścieżkę wywołania” w pamięci komputera. Finalnie opisz w komentarzu zaobserwowane rezultaty eksperymentu.
Integracja wszystkich poznanych mechanizmów: dziedziczenia, metod wirtualnych oraz parametrów params. Student buduje kompleksowy system, który wykazuje korzyści płynące z łączenia różnych zaawansowanych technik programistycznych w C#.
Jako lider zespołu IT w firmie kurierskiej, przygotowujesz mechanizm do wyliczania kosztów transportu różnych typów paczek. Stwórz główną klasę "Przesylka" z wirtualną metodą ObliczKoszt() oraz właściwością Waga. Następnie zaimplementuj klasy "PaczkaStandardowa" i "PrzesylkaEkspresowa", które w różny sposób nadpisują algorytm cenowy (np. ekspres dodaje stałą opłatę za priorytet). Dodatkowo Twoja klasa bazowa powinna posiadać konstruktor, który przyjmuje wagę i przekazuje ją do pola chronionego (protected). Kolejnym poziomem trudności jest stworzenie klasy statycznej "Logistyka", która zawiera metodę WyslijZbiorczo. Ta metoda powinna przyjmować parametr params Przesylka[] i obliczać całkowity koszt wysłania wszystkich paczek przekazanych w jednym wywołaniu. Dzięki polimorfizmowi, metoda ta będzie działać poprawnie niezależnie od tego, czy wyślemy jej trzy paczki zwykłe, czy pięć ekspresowych. System musi wyświetlić szczegółowy raport z podróży zbiorczej, listując każdą paczkę i jej wyliczony indywidualnie koszt. Taki projekt uczy, jak budować elastyczne interfejsy programistyczne odporne na zmiany wymagań biznesowych. Program kończy się wyświetleniem wielkiego podsumowania dla całej floty wysyłkowej.