Podstawy C# konsola

  • Lekcja 2. Środowisko programowania C# Tworzymy pierwszy program
  • Lekcja 3. Zmienne. Typy zmiennych
  • Lekcja 4. Operatory w C#
  • Lekcja 5. Instrukcje warunkowe (decyzje w programie)
  • Lekcja 6. Pętle (Powtarzanie zadań)
  • Lekcja 7. Funkcje i Metody (Budowanie własnych narzędzi)
  • Lekcja 8. Tablice i Kolekcje (Przechowywanie wielu danych)
  • Lekcja 9. Praca z Plikami (Odczyt i Zapis danych, w tym JSON)

Lekcja 1. Czym jest język C#? Typowanie i kompilacja vs interpretacja

C# to nowoczesny, obiektowy język programowania opracowany przez firmę Microsoft w 2000 roku. Jego twórcą jest Anders Hejlsberg, znany też z pracy nad językiem Delphi. C# został zaprojektowany do tworzenia różnorodnych aplikacji – od konsolowych, przez aplikacje desktopowe, webowe, mobilne, aż po gry komputerowe (np. w silniku Unity).

Założenia C#:

  • Czytelność: kod powinien być przejrzysty i zrozumiały nawet po czasie,
  • Obiektowość: wszystko opiera się na klasach i obiektach,

    Co to jest programowanie obiektowe?
    Wyobraź sobie, że program to świat złożony z różnych rzeczy, które coś robią i mają jakieś cechy. Na przykład:
    Samochód – ma kolor, markę i potrafi jechać
    Uczeń – ma imię, wiek i potrafi się uczyć
    Telefon – ma model, producenta i potrafi dzwonić
    Każda z tych rzeczy to obiekt.

    Co to jest klasa?
    Klasa to przepis, jak stworzyć daną rzecz. Tak jak:
    przepis na pizzę mówi, co trzeba przygotować i jak to złożyć,
    klasa Samochod mówi, że samochód ma kolor, markę i może jechać.

    Co to jest obiekt?
    Obiekt to konkretna rzecz stworzona według przepisu (klasy).
  • Bezpieczeństwo: język chroni programistę przed błędami typów i operacji nielegalnych,
  • Modularność: aplikacje można budować z wielu współpracujących elementów,
  • Wieloplatformowość: dzięki .NET i .NET MAUI można pisać aplikacje działające na Windows, Androidzie, Macu itd.,
  • Kompilacja: kod C# jest kompilowany do kodu pośredniego (IL), który uruchamiany jest przez środowisko .NET (CLR).

Typowanie i kompilacja vs interpretacja

Czym jest typowanie? Typowanie to zasada przypisywania określonego typu danym, np. czy dana zmienna to liczba, tekst, wartość logiczna itp. Typowanie określa, co można zrobić z daną zmienną i jakie operacje są dozwolone.

Statyczne typowanie (C#):

  • Typ zmiennej jest określany na etapie pisania programu,
  • Program nie skompiluje się, jeśli wystąpi niezgodność typów,
  • Przykład: int liczba = "tekst"; – błąd kompilacji.

Dynamiczne typowanie (Python):

  • Typ zmiennej określany jest dopiero w trakcie działania programu,
  • Więcej swobody, ale większe ryzyko błędów w czasie uruchomienia,
  • Przykład: liczba = "tekst" – działa, dopiero później może dać błąd.

Statyczne typowanie: przyspiesza wykrywanie błędów już na etapie kompilacji, co z kolei prowadzi do mniej błędów w czasie wykonywania.

Dynamiczne typowanie: mimo że daje większą elastyczność, może prowadzić do trudności w utrzymaniu kodu w większych projektach.

Kompilacja (C#):

  • Kod źródłowy jest najpierw przekształcany w kod pośredni (IL), a potem wykonywany przez maszynę wirtualną (CLR),
  • Błędy wykrywane już na etapie kompilacji,
  • Szybsze działanie programu po uruchomieniu.

Interpretacja (Python):

  • Kod wykonywany jest linijka po linijce bez wcześniejszego kompilowania,
  • Błędy widoczne dopiero w czasie uruchamiania programu,
  • Dobrze do szybkiego prototypowania, ale wolniejsze wykonywanie.

C# vs Python – podobieństwa i różnice:

CechaC#Python
TypowanieStatyczneDynamiczne
SkładniaBardziej formalna, wymaga deklaracjiLuźna, bardziej zbliżona do języka naturalnego
WydajnośćWyższa (kompilowany)Niższa (interpretowany)
ZastosowaniaAplikacje Windows, Unity, MAUISkrypty, data science, web, automatyzacja
Obsługa projektówWymaga tworzenia struktury projektuMożna pisać pojedyncze pliki

Uwaga dla .NET 8/9 (top-level statements)
W nowych projektach C# metoda Main() może być niewidoczna, bo kompilator generuje ją „w tle”. Taki jednolinijkowy program:

Console.WriteLine("Witaj w C#!");

jest kompletnym programem. To normalne w .NET 6+ (w tym .NET 8/9).

Lekcja 2. Środowisko programowania C# Tworzymy pierwszy program

Możemy pisać programy w C# w dwóch popularnych środowiskach:

1. Visual Studio Code (VS Code):

  • Lekkie, darmowe środowisko
  • Wymaga instalacji SDK .NET i rozszerzenia C#
  • Projekty tworzy się przez terminal:
mkdir KalkulatorSrednich
cd KalkulatorSrednich
dotnet new console

Aby uruchomić program wpisujemy w konsoli

dotnet run

2. Visual Studio 2022:

  • Rozbudowane środowisko z graficznym interfejsem
  • Wybieramy: Nowy projekt > Aplikacja konsolowa (.NET)
  • Nie trzeba używać terminala

Struktura projektu C# (zarówno w VS Code, jak i VS 2022):

Folder/plikCo robi?Co uczeń powinien wiedzieć?
Program.csTwój kodTu piszesz program
.csproj„plan projektu”Mówi .NET, co i jak ma być kompilowane
bin/„fabryka wyników”Tu trafia gotowy .exe
obj/„śmieci techniczne”Nie ruszamy – używa to kompilator

Global using
W projektach .NET 8/9 wiele using jest dodawanych globalnie (np. w GlobalUsings.cs). Kod zadziała nawet, gdy nie widać using System;.

KalkulatorSrednich/
├── Program.cs        ← główny plik z kodem
├── KalkulatorSrednich.csproj ← plik konfiguracyjny projektu
├── bin/              ← pliki wynikowe (.exe, .dll) – działa tylko po kompilacji
│   └── Debug/
│       └── net8.0/
│           └── KalkulatorSrednich.exe
├── obj/              ← pliki robocze kompilatora (.cache, .pdb)

Pierwszy program

Teoria: Program w C# zawsze zaczyna się od klasy z metodą Main(), która jest punktem wejścia.
Ale UWAGA! Nowe szablony w .NET 8/9
Szablon „Aplikacja konsolowa” tworzy program z ukrytym Main() (top-level statements). Dlatego możesz nie widzieć klasy Program. To nie błąd.

Kod „Hello World”:

using System; // dodanie przestrzeni nazw

class Program
{
    static void Main()
    {
        Console.WriteLine("Witaj, świecie!"); // wypisanie tekstu
    }
}

Wyjaśnienie linijek:

  • using System; – pozwala korzystać z podstawowych klas jak Console (załaduj „bibliotekę” do wypisywania na ekran (Console.WriteLine) – innymi słowy – to instrukcja, która „importuje” lub „włącza” przestrzeń nazw System, która zawiera wiele podstawowych klas, takich jak Console. To trochę jak otwieranie książki kucharskiej, w której znajdują się przepisy na dania (funkcje).
  • class Program – definicja klasy, wszystko w C# musi być w klasie (tworzymy klasę o nazwie Program – kontener na kod)
  • static void Main() – Główna metoda – to właśnie ona się uruchamia na start
  • Console.WriteLine(...) – wypisuje tekst do konsoli

Wejście – wyjście danych:

Console.Write("Podaj swoje imię: ");
string imie = Console.ReadLine();
Console.WriteLine($"Cześć, {imie}!");

Console.Write(„Podaj swoje imię: „); – To wyświetlenie komunikatu na ekranie bez przechodzenia do nowej linii. – Konsola wypisze Podaj swoje imię:  i czeka dalej w tej samej linii.

string imie = Console.ReadLine(); – To pobranie danych wpisanych przez użytkownika z klawiatury (np. “Grzesiek”). – Wszystko co wpisze użytkownik i zatwierdzi Enterem, trafia do zmiennej imie jako tekst (string).

Console.ReadLine() zawsze zwraca tekst, więc jeśli chcesz liczbę, musisz go potem przekonwertować np. int.Parse().

  • Console.WriteLine(…) – wypisuje tekst i przechodzi do nowej linii.
  • $”Cześć, {imie}!” – to łańcuch interpolowany, czyli taki, w którym możesz bezpośrednio wkładać zmienne w tekst, np. {imie} zostanie zamienione na „Grzesiek”.
  • Efekt: program wypisze np. Cześć, Grzesiek!

Podsumowując

CzynnośćKodCo robi?
Wyświetlenie tekstuConsole.Write()Pisze coś na ekranie (bez nowej linii)
Pobranie danychConsole.ReadLine()Czeka, aż użytkownik coś wpisze
Zapis do zmiennejstring zmienna = …Przechowuje to, co wpisał użytkownik
Wyświetlenie z wynikiemConsole.WriteLine(…)Pisze coś + przechodzi do nowej linii

PODSUMOWANIE:
Z czego składa się program w C#? (czyli krótka tabela do zapamiętania)

ElementCo to jest?PrzykładOpis uczniowski
usingdyrektywausing System;Włącza gotową bibliotekę funkcji, np. Console.WriteLine
classklasaclass Program„Pudełko” na kod – każda funkcja musi być w klasie
Main()metodastatic void Main()Start programu – pierwsze, co się uruchamia
Console.WriteLine()funkcjaConsole.WriteLine(„Hello”)Wyświetla coś w konsoli i przechodzi do nowej linii
Console.ReadLine()funkcjastring imie = Console.ReadLine();Pobiera dane wpisane z klawiatury
string, int, booltyp danychint wiek = 17;Określa rodzaj danych: tekst, liczba, prawda/fałsz

Porównanie C# vs Python – mały test logiczny

ScenariuszC#PythonUwaga
Przypisanie tekstu do liczby❌ Błąd kompilacji (int x = „tekst”;)✅ Przejdzie (x = „tekst”)Python sprawdzi dopiero w trakcie działania
Brak Main()❌ Program nie działa✅ Kod działa od góry do dołuC# zawsze potrzebuje Main()
Brak średnika ;❌ Błąd kompilacji✅ Python nie używa średnikówW C# każdy rozkaz kończysz ;
Można pisać pojedynczy plik?❌ Lepiej mieć strukturę projektu✅ Wystarczy 1 plikC# wymaga .csproj, plików klas itd.

Zadanie na podsumowanie.

Napisz program, który:

  1. Prosi użytkownika o imię
  2. Wita użytkownika imieniem
  3. Pyta o wiek
  4. Wypisuje komunikat: „Twoje imię, masz 17 lat. Za rok będziesz mieć 18!”

A oto przykład rozwiązania. Przeanalizuj poniższy kod i wykonaj dialog pomiędzy programem, a użytkownikiem.

using System;

class Program
{
    static void Main()
    {
        Console.Write("Podaj swoje imię: ");
        Console.ReadLine(); // Uczeń jeszcze nie wie, że można to zapisać do zmiennej

        Console.WriteLine("Cześć!");

        Console.Write("Ile masz lat? ");
        Console.ReadLine();

        Console.WriteLine("Twoje imię, masz 17 lat. Za rok będziesz mieć 18!");
    }
}

Jak to się ma do desktopu i MAUI

  • Konsola: start w Program.cs.
  • WinForms/WPF: logika startu w App.xaml/Form1.cs.
  • .NET MAUI: konfiguracja w MauiProgram.cs, a aplikacja w App.xaml.cs.
    Składnia C# i logika są te same – zmienia się tylko sposób interakcji (konsola vs kontrolki UI).

Lekcja 3. Zmienne. Typy zmiennych

Co to jest zmienna?

Zmienna to kontener na dane. Można ją sobie wyobrazić jako pojemnik z etykietą, który trzyma jakąś wartość.

Wyobraź sobie pudełko opisane jako „wiek”. W środku zapisujesz liczbę 17. To właśnie jest zmienna: ma nazwę (np. wiek) i przechowuje wartość (np. 17).

💡 W programie zmienna to zaadresowana część pamięci komputera, gdzie przechowywana jest informacja. Komputer musi wiedzieć:
* jak nazywa się ta zmienna

* jakiego typu są dane (czy to tekst? liczba? prawda/fałsz?)

* jaka jest aktualna wartość


Typy danych – czyli co może zawierać zmienna

C# wymaga, by każda zmienna miała określony typ – to trochę jak etykieta na pudełku: „tu są tylko liczby całkowite” albo „tu może być tylko tekst”.

Rodzaje zmiennych w C# (Typy danych).

1. Typy proste (typy wartościowe – Value Types)

TypOpisPrzykład
intLiczba całkowita (4 bajty)int liczba = 42;
longDługa liczba całkowita (8 bajtów)long duza = 1234567890;
shortKrótsza liczba całkowita (2 bajty)short male = 32000;
byteLiczba od 0 do 255 (1 bajt)byte bajt = 255;
sbyteLiczba -128 do 127 (1 bajt)sbyte liczba = -100;
floatLiczba zmiennoprzecinkowa (4 bajty)float f = 3.14f;
doubleDokładniejsza liczba (8 bajtów)double d = 3.141592;
decimalDokładne liczby (np. do pieniędzy)decimal cena = 99.99m;
boolWartość logiczna (true/false)bool prawda = true;
charJeden znak (literka)char znak = 'A’;


2. Typy tekstowe (Reference Types)

TypOpisPrzykład
stringTekst (ciąg znaków)string imie = „Ania”;
objectMoże przechować cokolwiekobject cos = 5;

object to najbardziej ogólny typ – każda zmienna w C# pochodzi od object.


3. Typy złożone – mniej spotykane na początku nauki

TypOpisPrzykład
DateTimeData i godzinaDateTime teraz = DateTime.Now;
TimeSpanRóżnica czasuTimeSpan t = TimeSpan.FromHours(2);
GuidUnikalny identyfikatorGuid id = Guid.NewGuid();
varAutomatyczne wykrycie typuvar x = 5; (tu x to int)


Deklarowanie zmiennych – jak to się pisze?

// tekst
string imie = "Ania";

// liczba całkowita
int wiek = 18;

// liczba z przecinkiem
double temperatura = 36.6;

// prawda/fałsz
bool czyPada = true;

// jeden znak
char litera = 'B';

Konwersja typów – zamiana jednej zmiennej na inny typ

Czasem trzeba zamienić tekst na liczbę, albo liczbę na tekst. Oto jak to robimy:

Tekst →Liczba (string→ int, double)

string wpis = "25";
int liczba = int.Parse(wpis);

string wpis2 = "3.14";
double liczb_Z_Przecinkiem = double.Parse(wpis2);

Uwaga: int.Parse() zadziała tylko, jeśli wpisany tekst to poprawna liczba. W przeciwnym razie program się wywali.

 Liczba Tekst (int, double → string)

int liczba = 42;
string tekst = liczba.ToString();

double d = 3.14;
string napis = d.ToString();

Praktycznie w UI: TryParse
W aplikacjach okienkowych i mobilnych dane z pól tekstowych bywają błędne. Zamiast int.Parse(...) używaj:

if (int.TryParse(tekst, out int liczba)) { /* OK */ } else { /* komunikat o błędzie */ }

Unikasz wyjątków i masz czytelny warunek.


Przykładowy program – użycie różnych typów zmiennych

using System;

class Program
{
    static void Main()
    {
        string imie = "Grzesiek";
        int wiek = 17;
        double temperatura = 36.6;
        bool czyZdrowy = true;
        char ocena = 'A';

        Console.WriteLine("Imię: " + imie);
        Console.WriteLine("Wiek: " + wiek);
        Console.WriteLine("Temperatura: " + temperatura + " °C");
        Console.WriteLine("Zdrowy? " + czyZdrowy);
        Console.WriteLine("Ocena z informatyki: " + ocena);
    }
}

Zadanie 1 – Zamień tekst na liczbę

Program pyta użytkownika o jego wiek, wpisany jako tekst, a potem zamienia go na liczbę i wypisuje:

Masz 18 lat, czyli za 2 lata będziesz mieć 20.


Zadanie 2 – Liczba na tekst

Zapisz liczbę 42 do zmiennej int, a potem wypisz ją jako tekst w zdaniu:

„Twoja szczęśliwa liczba to 42” – ale liczba musi być częścią string, nie int.


Zadanie 3 – Prawda czy fałsz

Napisz program, który ma zmienną bool czyUczen = true;

Jeśli czyUczen jest true, wypisz: „Witaj uczniu!”, jeśli false – „To program tylko dla uczniów!”

Lekcja 4. Operatory w C#

W poprzedniej lekcji nauczyliśmy się, czym są zmienne i typy danych – czyli jak przechowywać różne rodzaje informacji. Ale co, jeśli chcemy coś z tymi danymi zrobić? Dodać liczby, porównać teksty, sprawdzić warunki? Do tego służą operatory!

Co to jest operator?

Wyobraź sobie, że operator to symbol lub słowo kluczowe, które wykonuje jakąś operację na jednej lub więcej wartościach (zwanych operandami). To trochę jak znaki matematyczne w szkole (+,−,×,÷) – mówią nam, co zrobić z liczbami.

W C# mamy wiele rodzajów operatorów. Przyjrzyjmy się najważniejszym z nich.

1. Operatory arytmetyczne (do liczenia)

Te operatory służą do wykonywania podstawowych działań matematycznych.

OperatorOpisPrzykładWynik
+Dodawanieint a = 5 + 3;a to 8
Odejmowanieint b = 10 – 4;b to 6
*Mnożenieint c = 2 * 6;c to 12
/Dzieleniedouble d = 10.0 / 4.0;d to 2.5
int e = 10 / 4;e to 2 (dzielenie całkowite)
%Reszta z dzielenia (modulo)int f = 10 % 3;f to 1 (bo 10=3×3+1)

Ważne o dzieleniu (/):

  • Jeśli dzielisz dwie liczby całkowite (typu int), wynikiem zawsze będzie liczba całkowita (część po przecinku zostanie odcięta).
  • Jeśli chcesz uzyskać wynik z częścią dziesiętną, użyj liczb zmiennoprzecinkowych (np. double lub float).

Przykład:

using System;

class Program
{
    static void Main()
    {
        int liczba1 = 15;
        int liczba2 = 4;

        // Dodawanie
        int suma = liczba1 + liczba2;             // suma = 19
        Console.WriteLine($"Suma: {suma}");

        // Odejmowanie
        int roznica = liczba1 - liczba2;          // roznica = 11
        Console.WriteLine($"Różnica: {roznica}");

        // Mnożenie
        int iloczyn = liczba1 * liczba2;          // iloczyn = 60
        Console.WriteLine($"Iloczyn: {iloczyn}");

        // Dzielenie całkowite
        int ilorazCalkowity = liczba1 / liczba2; 
                                          // ilorazCalkowity = 3
        Console.WriteLine($"Iloraz całkowity: {ilorazCalkowity}");

        // Dzielenie zmiennoprzecinkowe (musimy skonwertować jedną z liczb na double)
        double ilorazDokladny = (double)liczba1 / liczba2; 
                                         // ilorazDokladny = 3.75
        Console.WriteLine($"Iloraz dokładny: {ilorazDokladny}");

        // Reszta z dzielenia
        int reszta = liczba1 % liczba2;            // reszta = 3
        Console.WriteLine($"Reszta z dzielenia: {reszta}");
    }
}

2. Operatory przypisania (do zapisywania wartości)

Operator przypisania (=) już znasz – używasz go do nadawania wartości zmiennym. Ale istnieją też skrócone wersje, które łączą operację arytmetyczną z przypisaniem.

OperatorOpisPrzykładJest to samo co
#ERROR!Proste przypisanieint x = 10;x staje się 10
+=Dodaj i przypiszx += 5;x = x + 5; (dodaj 5 do x)
-=Odejmij i przypiszx -= 2;x = x – 2; (odejmij 2 od x)
*=Pomnóż i przypiszx *= 3;x = x * 3; (pomnóż x przez 3)
/=Podziel i przypiszx /= 2;x = x / 2; (podziel x przez 2)
%=Reszta z dzielenia i przypiszx %= 4;x = x % 4; (oblicz resztę i przypisz do x)

Przykład:

using System;

class Program
{
    static void Main()
    {
        int punkty = 100;
        Console.WriteLine($"Początkowe punkty: {punkty}"); // 
                                        Początkowe punkty: 100

        punkty += 50; // punkty = punkty + 50; czyli punkty = 150
        Console.WriteLine($"Punkty po dodaniu: {punkty}"); // 
                                        Punkty po dodaniu: 150

        punkty -= 20; // punkty = punkty - 20; czyli punkty = 130
        Console.WriteLine($"Punkty po odjęciu: {punkty}"); // 
                                        Punkty po odjęciu: 130

        punkty *= 2; // punkty = punkty * 2; czyli punkty = 260
        Console.WriteLine($"Punkty po pomnożeniu: {punkty}"); // 
                                        Punkty po pomnożeniu: 260

        punkty /= 10; // punkty = punkty / 10; czyli punkty = 26
        Console.WriteLine($"Punkty po podzieleniu: {punkty}"); // 
                                        Punkty po podzieleniu: 26
    }
}

3. Operatory inkrementacji i dekrementacji (szybkie +/- 1)

Służą do zwiększania lub zmniejszania wartości zmiennej o jeden.

OperatorOpisPrzykładJest to samo co
++Zwiększenie o 1 (inkrementacja)x++;x = x + 1; lub x += 1;
Zmniejszenie o 1 (dekrementacja)x–;x = x – 1; lub x -= 1;

Pre-inkrementacja/dekrementacja vs Post-inkrementacja/dekrementacja

To trochę bardziej zaawansowane, ale ważne!

  • ++x (pre-inkrementacja): Najpierw zwiększa wartość x o 1, a potem używa nowej wartości.
  • x++ (post-inkrementacja): Najpierw używa obecnej wartości x, a potem zwiększa x o 1.

Analogicznie działa to dla --.

Przykład:

using System;

class Program
{
    static void Main()
    {
        int i = 5;
        Console.WriteLine($"Początkowe i: {i}");         // 5

        // Post-inkrementacja: użyj, a potem zwiększ
        int j = i++;
        Console.WriteLine($"Po j = i++ : j={j}, i={i}"); // j=5, 
                                                            i=6

        int k = 10;
        Console.WriteLine($"Początkowe k: {k}");         // 10

        // Pre-inkrementacja: zwiększ, a potem użyj
        int l = ++k;
        Console.WriteLine($"Po l = ++k : l={l}, k={k}"); // l=11, 
                                                            k=11
    }
}

4. Operatory porównania (do sprawdzania równości/różnic)

Te operatory służą do porównywania dwóch wartości. Zawsze zwracają wartość logiczną (bool): true (prawda) lub false (fałsz). Są bardzo ważne w instrukcjach warunkowych (np. if, które poznamy później).

OperatorOpisPrzykładWynik (jeśli a=5, b=10)
#ERROR!Jest równea == bFALSE
!=Jest różnea != bTRUE
>Jest większe niża > bFALSE
<Jest mniejsze niża < bTRUE
>=Jest większe lub równe niża >= bFALSE
<=Jest mniejsze lub równe niża <= bTRUE

Zobaczmy to w przykładzie:

using System;

class Program
{
    static void Main()
    {
        int wiekUcznia = 16;
        int wymaganyWiek = 18;

        bool czyPelnoletni = wiekUcznia >= wymaganyWiek;
        Console.WriteLine($"Czy uczeń jest pełnoletni? {czyPelnoletni}");                                  // false

        string imie1 = "Adam";
        string imie2 = "adam";
        string imie3 = "Adam";

        bool czyImionaTakieSame = (imie1 == imie2); // Porównuje 
                                                 wielkość liter!
        Console.WriteLine($"Czy Adam == adam? {czyImionaTakieSame}");                            // false

        bool czyImionaTakieSame2 = (imie1 == imie3);
        Console.WriteLine($"Czy Adam == Adam? {czyImionaTakieSame2}");                           // true
    }
}

Pamiętaj: == to operator porównania (czy coś jest równe czemuś), natomiast = to operator przypisania (nadaj wartość). To częsty błąd początkujących!


5. Operatory logiczne (do łączenia warunków)

Te operatory pozwalają łączyć lub negować wyniki operatorów porównania. Również zwracają wartość bool.

OperatorOpisPrzykładWynik (jeśli x=5, y=10)
&&AND (i)x > 0 && y > 0true (bo oba warunki są prawdziwe)
``OR (lub)`x > 10
!NOT (negacja/nie)!(x == y)true (bo x == y jest fałszem, a ! go neguje)

Wyjaśnienie:

  • && (AND): Wynik jest true tylko wtedy, gdy wszystkie połączone warunki są true. Jeśli choć jeden jest false, cały wynik to false.
  • || (OR): Wynik jest true, jeśli choć jeden z połączonych warunków jest true. Wynik jest false tylko wtedy, gdy wszystkie warunki są false.
  • ! (NOT): Zmienia wartość logiczną na przeciwną (true na false, false na true).

Przykład:

using System;

class Program
{
    static void Main()
    {
        int wiek = 18;
        bool maPrawoJazdy = true;
        bool maSamochod = false;

                                 // Czy może prowadzić? (jest 
                                  pełnoletni ORAZ ma prawo jazdy)
        bool mozeProwadzic = (wiek >= 18 && maPrawoJazdy);
        Console.WriteLine($"Czy może prowadzić? {mozeProwadzic}");               // true

                                 // Czy może jechać na wakacje? 
                                 (ma prawo jazdy LUB ma samochód)
        bool mozeJechaNaWakacje = (maPrawoJazdy || maSamochod);
        Console.WriteLine($"Czy może jechać na wakacje? {mozeJechaNaWakacje}");          // true

                                 // Czy NIE ma samochodu?
        bool nieMaSamochodu = !maSamochod;
        Console.WriteLine($"Czy nie ma samochodu? {nieMaSamochodu}");              // true
    }
}

Podsumowanie Operatorów:

Rodzaj operatoraCo robi?Przykłady
ArytmetyczneObliczenia liczbowe+, -, *, /, %
PrzypisaniaNadaje wartość zmiennej#ERROR!
Inkrementacji/ DekrementacjiZwiększa/zmniejsza o 1++, —
PorównaniaPorównuje wartości, zwraca true/false#ERROR!
LogiczneŁączy warunki logiczne&& (AND), `

Zadania na podsumowanie.

Teraz czas na praktykę! Użyj operatorów, aby rozwiązać te proste zadania.

Zadanie 1 – Prosty kalkulator Napisz program, który:

  1. Pyta użytkownika o pierwszą liczbę całkowitą.
  2. Pyta użytkownika o drugą liczbę całkowitą.
  3. Wyświetla sumę tych liczb, różnicę, iloczyn, iloraz (dokładny, jako liczba zmiennoprzecinkowa!) i resztę z dzielenia.

Przykład działania:

Podaj pierwszą liczbę: 10
Podaj drugą liczbę: 3
Suma: 13
Różnica: 7
Iloczyn: 30
Iloraz: 3.3333333333333335
Reszta z dzielenia: 1

Zadanie 2 – Stan baterii Napisz program, który:

  1. Pyta użytkownika o aktualny poziom baterii (np. 85, czyli 85%).
  2. Pyta, czy telefon jest podłączony do ładowarki (odpowiedzi: tak lub nie).
  3. Wyświetla komunikat:
    • „Telefon jest naładowany!” jeśli bateria jest równa lub większa niż 90% LUB jest podłączony do ładowarki.
    • „Telefon wymaga ładowania.” w przeciwnym wypadku.

Wskazówka: Do pytania o ładowarkę użyj Console.ReadLine() i porównaj wynik (string!). Pamiętaj o konwersji tekstu na liczbę dla poziomu baterii.


Zadanie 3 – Punkty w grze Napisz program, który symuluje zdobywanie punktów w prostej grze:

  1. Zadeklaruj zmienną int punkty = 0;.
  2. Dodaj do punkty wartość 100 używając operatora +=.
  3. Następnie zwiększ punkty o 1 używając operatora ++.
  4. Wyświetl aktualną liczbę punktów.
  5. Odejmij 25 punktów używając operatora -=.
  6. Wyświetl nową liczbę punktów.

Lekcja 5. Instrukcje warunkowe (decyzje w programie)

W poprzedniej lekcji poznaliśmy operatory, które pozwalają nam wykonywać obliczenia i porównywać wartości. Ale co, jeśli chcemy, żeby nasz program reagował inaczej w zależności od tego, jaki jest wynik tych porównań? Na przykład, żeby wyświetlał „Witaj, dorosły!”, jeśli wiek jest większy niż 18, a „Witaj, młodzieńcze!” w innym przypadku?

Do tego służą instrukcje warunkowe! To one sprawiają, że program potrafi podejmować „decyzje” i wykonywać różne bloki kodu w zależności od spełnienia określonych warunków.

Co to jest instrukcja warunkowa?

Wyobraź sobie, że instrukcja warunkowa to trochę jak rozwidlenie drogi z tabliczkami: „Jeśli pada deszcz, idź tą drogą. W przeciwnym razie idź inną.”

W programowaniu instrukcje warunkowe pozwalają nam określić, że jeśli pewien warunek jest prawdziwy, to wykonaj jeden zestaw instrukcji, a w przeciwnym razie (jeśli warunek jest fałszywy) – wykonaj inny zestaw instrukcji.

1. Instrukcja if (Jeśli coś)

To najbardziej podstawowa instrukcja warunkowa. Pozwala wykonać dany kod tylko wtedy, gdy określony warunek jest prawdziwy (true).

Schemat działania:

if (WARUNEK)
{
    // Ten kod zostanie wykonany TYLKO jeśli WARUNEK jest prawdziwy
}

  • if: Słowo kluczowe oznaczające początek instrukcji warunkowej.
  • (WARUNEK): W nawiasach podajesz warunek logiczny, czyli wyrażenie, które zwraca true (prawda) lub false (fałsz). Najczęściej używasz tutaj operatorów porównania (==, >, < itp.) i operatorów logicznych (&&, ||, !).
  • { ... }: Blok kodu w nawiasach klamrowych zostanie wykonany tylko wtedy, gdy warunek jest prawdziwy. Jeśli warunek jest fałszywy, ten blok kodu zostanie pominięty.

Przykład:

using System;

class Program
{
    static void Main()
    {
        int temperatura = 25;

        // Jeśli temperatura jest większa niż 20, wyświetl komunikat o cieple
        if (temperatura > 20)
        {
            Console.WriteLine("Jest ciepło!");
        }

        Console.WriteLine("Koniec sprawdzania temperatury.");
        // Jeśli temperatura byłaby np. 15, komunikat "Jest ciepło!" by się nie wyświetlił.
    }
}

2. Instrukcja if-else (Jeśli coś, w przeciwnym razie coś innego)

Co, jeśli chcemy wykonać coś innego, gdy warunek jest fałszywy? Do tego służy else (w przeciwnym razie).

Schemat działania:

if (WARUNEK)
{
    // Ten kod zostanie wykonany, jeśli WARUNEK jest prawdziwy
}
else
{
    // Ten kod zostanie wykonany, jeśli WARUNEK jest fałszywy (czyli "w przeciwnym razie")
}

Przykład:

using System;

class Program
{
    static void Main()
    {
        int wiek = 17;

        if (wiek >= 18) // Sprawdzamy, czy wiek jest 18 lub więcej
        {
            Console.WriteLine("Jesteś pełnoletni!");
        }
        else // W przeciwnym razie (czyli wiek jest mniejszy niż 18)
        {
            Console.WriteLine("Jesteś niepełnoletni.");
        }

        Console.WriteLine("Dziękujemy za sprawdzenie wieku.");
    }
}

3. Instrukcja if-else if-else (Wiele możliwości)

A co, jeśli mamy więcej niż dwa możliwe scenariusze? Na przykład, chcemy ocenić wynik egzaminu: „Znakomicie”, „Dobrze”, „Dostatecznie” lub „Poprawka”. Możemy to zrobić, łącząc ze sobą instrukcje if i else if.

Schemat działania:

if (PIERWSZY_WARUNEK)
{
    // Ten kod, jeśli PIERWSZY_WARUNEK jest prawdziwy
}
else if (DRUGI_WARUNEK) // Sprawdzamy ten, TYLKO jeśli PIERWSZY_WARUNEK był fałszywy
{
    // Ten kod, jeśli DRUGI_WARUNEK jest prawdziwy
}
else if (TRZECI_WARUNEK) // Sprawdzamy ten, TYLKO jeśli PIERWSZY i DRUGI były fałszywe
{
    // Ten kod, jeśli TRZECI_WARUNEK jest prawdziwy
}
else // Jeśli ŻADEN z powyższych warunków nie był prawdziwy
{
    // Ten kod zostanie wykonany
}

Ważne: Program sprawdzi warunki po kolei. Jak tylko znajdzie pierwszy warunek, który jest prawdziwy, wykona odpowiadający mu blok kodu i zakończy całą sekwencję if-else if-else. Pozostałe warunki nie będą już sprawdzane!

Przykład:

using System;

class Program
{
    static void Main()
    {
        int wynikEgzaminu = 75; // Wynik w procentach

        if (wynikEgzaminu >= 90)
        {
            Console.WriteLine("Ocena: Znakomicie!");
        }
        else if (wynikEgzaminu >= 70) // Czyli wynik jest >=70 ALE <90
        {
            Console.WriteLine("Ocena: Dobrze!");
        }
        else if (wynikEgzaminu >= 50) // Czyli wynik jest >=50 ALE <70
        {
            Console.WriteLine("Ocena: Dostatecznie.");
        }
        else // Czyli wynik jest mniejszy niż 50
        {
            Console.WriteLine("Ocena: Poprawka!");
        }

        Console.WriteLine("Sprawdzanie ocen zakończone.");
    }
}

4. Instrukcja switch (Wybór z listy)

Instrukcja switch to alternatywa dla długich łańcuchów if-else if, gdy chcemy sprawdzić jedną zmienną pod kątem wielu konkretnych wartości. Działa najlepiej, gdy masz do porównania proste typy, takie jak liczby całkowite, znaki (char) lub tekst (string).

Schemat działania:

switch (ZMIENNA_DO_SPRAWDZENIA)
{
    case WARTOSC1:
        // Kod do wykonania, jeśli ZMIENNA_DO_SPRAWDZENIA jest 
           równa WARTOSC1
        break; // Ważne! Kończy działanie switch

    case WARTOSC2:
        // Kod do wykonania, jeśli ZMIENNA_DO_SPRAWDZENIA jest 
           równa WARTOSC2
        break;

    case WARTOSC3:
        // Kod do wykonania, jeśli ZMIENNA_DO_SPRAWDZENIA jest 
           równa WARTOSC3
        break;

    default: // Opcjonalnie: Jeśli zmienna nie pasuje do żadnego 
                z case'ów
            // Kod do wykonania w każdym innym przypadku
        break;
}

  • switch (ZMIENNA_DO_SPRAWDZENIA): W nawiasach podajesz zmienną, której wartość chcesz porównywać.
  • case WARTOSC:: Określa konkretną wartość. Jeśli zmienna pasuje do tej wartości, kod pod case zostanie wykonany.
  • break;: Jest bardzo ważny! Powoduje wyjście z instrukcji switch. Jeśli go zabraknie, program będzie kontynuował wykonywanie kodu w następnych case’ach (tzw. „fall-through”), co rzadko jest pożądane.
  • default:: Jest opcjonalny. Kod w default zostanie wykonany, jeśli wartość zmiennej nie pasuje do żadnego z case’ów. Działa podobnie do else w if-else.

Przykład:

using System;

class Program
{
    static void Main()
    {
        char wyborUzytkownika = 'B';

        Console.Write("Wybrano opcję: ");

        switch (wyborUzytkownika)
        {
            case 'A':
                Console.WriteLine("Opcja A.");
                break;
            case 'B':
                Console.WriteLine("Opcja B. Super wybór!");
                break;
            case 'C':
                Console.WriteLine("Opcja C.");
                break;
            default:
                Console.WriteLine("Nieznana opcja.");
                break;
        }

        Console.WriteLine("Koniec wyboru.");
    }
}

A tu Case ze zmienną:

string imie = "Ania";
switch (imie)
{
    case "Ania": Console.WriteLine("Cześć Aniu!"); break;
    case "Tomek": Console.WriteLine("Hej Tomku!"); break;
    default: Console.WriteLine("Cześć!"); break;
}

Podsumowanie instrukcji warunkowych:

InstrukcjaKiedy używamy?Przykład (ogólny)
ifJeśli kod ma być wykonany TYLKO gdy warunek jest prawdziwy.if (warunek) { … }
if-elseGdy są DWA scenariusze: jeden dla true, drugi dla false.if (warunek) { … } else { … }
if-else if-elseGdy mamy WIELE scenariuszy i sprawdzamy je po kolei.if (…) { … } else if (…) { … } else { … }
switchGdy sprawdzamy JEDNĄ zmienną pod kątem WIELU konkretnych wartości.switch (zmienna) { case X: … break; default: … }

Zadania na podsumowanie.

Użyj poznanych instrukcji warunkowych, aby rozwiązać poniższe zadania:

Zadanie 1 – Sprawdzanie parzystości Napisz program, który:

  1. Prosi użytkownika o podanie liczby całkowitej.
  2. Sprawdza, czy liczba jest parzysta czy nieparzysta.
    • Wskazówka: Liczba jest parzysta, jeśli reszta z dzielenia przez 2 wynosi 0 (% operator!).
  3. Wyświetla odpowiedni komunikat, np. „Liczba 7 jest nieparzysta.” lub „Liczba 10 jest parzysta.”

Zadanie 2 – Rabat w sklepie Napisz program, który:

  1. Pyta użytkownika o kwotę zakupu (np. double kwota = ...).
  2. Zastosuj następujące zasady rabatowe:
    • Jeśli kwota zakupu jest większa lub równa 200 zł, udziel 15% rabatu.
    • Jeśli kwota zakupu jest większa lub równa 100 zł, udziel 10% rabatu.
    • W przeciwnym razie (kwota mniejsza niż 100 zł), nie udzielaj rabatu.
  3. Wyświetl informację o zastosowanym rabacie (w procentach) oraz ostateczną kwotę do zapłaty.

Przykład działania:

Podaj kwotę zakupu: 150
Zastosowano rabat: 10%
Do zapłaty: 135 zł

Zadanie 3 – Dzień tygodnia Napisz program, który:

  1. Prosi użytkownika o podanie numeru dnia tygodnia (od 1 do 7, gdzie 1 to poniedziałek, 7 to niedziela).
  2. Używając instrukcji switch, wyświetla nazwę dnia tygodnia.
  3. Jeśli użytkownik poda numer spoza zakresu 1-7, wyświetl komunikat „Nieprawidłowy numer dnia.”

Przykład działania:

Podaj numer dnia tygodnia (1-7): 3
Dziś jest Środa.

Z konsoli do UI
W WinForms/WPF/MAUI warunki działają identycznie. Różni się tylko wejście/wyjście:

  • zamiast Console.ReadLine() czytasz z TextBox/Entry,
  • zamiast Console.WriteLine() ustawiasz Label.Text/TextBlock.Text.
    Logika if / else if / else zostaje bez zmian.

Lekcja 6. Pętle (Powtarzanie zadań)

W poprzednich lekcjach nauczyliśmy się, jak przechowywać dane w zmiennych i jak program może podejmować decyzje za pomocą instrukcji warunkowych. Ale co, jeśli chcemy wykonać ten sam fragment kodu wiele razy? Na przykład, wyświetlić liczby od 1 do 100, przetworzyć listę 1000 klientów, albo grać w grę, dopóki gracz nie przegra?

Pisanie tego samego kodu raz po raz byłoby bardzo żmudne i podatne na błędy. Na szczęście istnieją pętle!

Co to jest pętla?

Wyobraź sobie, że pętla to jak automat do powtarzania zadań. Dajesz mu instrukcję, a on wykonuje ją określoną liczbę razy lub tak długo, aż pewien warunek zostanie spełniony. Zamiast mówić „podnieś lewą nogę, opuść lewą nogę, podnieś prawą nogę, opuść prawą nogę…” sto razy, po prostu mówisz „idź naprzód 100 kroków”.

W programowaniu pętle pozwalają nam wielokrotnie wykonywać ten sam blok kodu, co oszczędza czas, zmniejsza ilość kodu i sprawia, że programy są bardziej elastyczne.

W C# mamy kilka rodzajów pętli, każda do nieco innych zastosowań. Poznajmy najważniejsze!


1. Pętla for (Ile razy?)

Pętla for jest idealna, gdy wiemy z góry, ile razy chcemy powtórzyć jakąś operację. Najczęściej używa się jej do iterowania (przechodzenia) przez sekwencje liczb.

Schemat działania:

for (INICJALIZACJA; WARUNEK_KONTYNUACJI; KROK_PO_KAZDEJ_ITERACJI)
{
    // Kod wewnątrz pętli - będzie wykonany w każdym powtórzeniu
}

  • INICJALIZACJA: Dzieje się tylko raz na początku pętli. Zazwyczaj tutaj deklarujemy i inicjalizujemy zmienną liczącą (np. int i = 0;).
  • WARUNEK_KONTYNUACJI: Ten warunek jest sprawdzany przed każdym powtórzeniem pętli. Jeśli warunek jest prawdziwy (true), pętla kontynuuje działanie. Jeśli jest fałszywy (false), pętla się zatrzymuje.
  • KROK_PO_KAZDEJ_ITERACJI: Dzieje się po każdym wykonaniu bloku kodu w pętli. Najczęściej jest to zwiększenie lub zmniejszenie zmiennej liczącej (np. i++ lub i--).

Przykład: Liczenie od 1 do 5

using System;

class Program
{
    static void Main()
    {
        Console.WriteLine("Liczymy od 1 do 5:");
        // int i = 1;      -> Startujemy od 1
        // i <= 5;       -> Kontynuujemy, dopóki i jest mniejsze lub równe 5
        // i++           -> Po każdym powtórzeniu zwiększamy i o 1
        for (int i = 1; i <= 5; i++)
        {
            Console.WriteLine(i); // Wypisze: 1, 2, 3, 4, 5
        }

        Console.WriteLine("\nLiczymy co 2 od 0 do 10:");
        for (int j = 0; j <= 10; j += 2) // Zwiększamy j o 2 w każdym kroku
        {
            Console.WriteLine(j); // Wypisze: 0, 2, 4, 6, 8, 10
        }

        Console.WriteLine("\nOdliczanie od 3 do 1:");
        for (int k = 3; k >= 1; k--) // Zmniejszamy k o 1 w każdym kroku
        {
            Console.WriteLine(k); // Wypisze: 3, 2, 1
        }
    }
}

2. Pętla while (Dopóki coś)

Pętla while jest używana, gdy chcemy powtarzać kod tak długo, jak pewien warunek jest prawdziwy. Nie wiemy z góry, ile razy pętla się wykona.

Schemat działania:

while (WARUNEK_KONTYNUACJI)
{
    // Kod wewnątrz pętli - będzie wykonany, dopóki WARUNEK_KONTYNUACJI jest prawdziwy
    // Ważne: Wewnątrz pętli musi być coś, co w końcu zmieni WARUNEK_KONTYNUACJI na false,
    // inaczej pętla będzie działać w nieskończoność (nieskończona pętla)!
}

  • while: Słowo kluczowe oznaczające pętlę.
  • (WARUNEK_KONTYNUACJI): Ten warunek jest sprawdzany przed każdym powtórzeniem. Jeśli jest true, kod w bloku {} się wykona. Jeśli false, pętla się zatrzymuje.

Przykład: Zgadywanie liczby

using System;

class Program
{
    static void Main()
    {
        Random losowaLiczba = new Random(); // Tworzymy obiekt do losowania liczb
        int szukanaLiczba = losowaLiczba.Next(1, 11); // Losujemy liczbę od 1 do 10
        int zgadnietaLiczba = 0;
        int proby = 0;

        Console.WriteLine("Zgadnij liczbę od 1 do 10!");

        // Dopóki zgadniętaLiczba nie jest równa szukanaLiczba
        while (zgadnietaLiczba != szukanaLiczba)
        {
            Console.Write("Twoja próba: ");
            string input = Console.ReadLine();
            zgadnietaLiczba = int.Parse(input); // Konwertujemy tekst na liczbę
            proby++;

            if (zgadnietaLiczba < szukanaLiczba)
            {
                Console.WriteLine("Za mało!");
            }
            else if (zgadnietaLiczba > szukanaLiczba)
            {
                Console.WriteLine("Za dużo!");
            }
        }

        Console.WriteLine($"Gratulacje! Zgadłeś liczbę {szukanaLiczba} w {proby} próbach.");
    }
}

3. Pętla do-while (Wykonaj raz, potem dopóki coś)

Pętla do-while jest podobna do while, ale ma jedną kluczową różnicę: blok kodu w {} zostanie wykonany przynajmniej raz, zanim warunek zostanie sprawdzony.

Schemat działania:

do
{
    // Kod wewnątrz pętli - zostanie wykonany przynajmniej raz
} while (WARUNEK_KONTYNUACJI); // Warunek sprawdzany JUZ PO wykonaniu bloku

  • do: Rozpoczyna blok kodu, który zawsze wykona się przynajmniej raz.
  • while (WARUNEK_KONTYNUACJI);: Warunek jest sprawdzany na końcu. Jeśli jest true, pętla powtarza się. Jeśli false, zatrzymuje się.

Przykład: Proste menu z potwierdzeniem

using System;

class Program
{
    static void Main()
    {
        string wybor;

        do
        {
            Console.WriteLine("--- MENU ---");
            Console.WriteLine("1. Nowa gra");
            Console.WriteLine("2. Wczytaj grę");
            Console.WriteLine("3. Opcje");
            Console.WriteLine("4. Wyjście");
            Console.Write("Wybierz opcję (1-4): ");
            wybor = Console.ReadLine(); // Pobieramy wybór użytkownika

            if (wybor == "1") Console.WriteLine("Rozpoczynam nową grę...");
            else if (wybor == "2") Console.WriteLine("Wczytuję grę...");
            else if (wybor == "3") Console.WriteLine("Otwieram opcje...");
            else if (wybor == "4") Console.WriteLine("Wychodzę z gry. Do zobaczenia!");
            else Console.WriteLine("Nieznany wybór, spróbuj ponownie.");

            // Pętla będzie działać, dopóki użytkownik nie wybierze opcji '4'
        } while (wybor != "4");

        Console.WriteLine("Program zakończony.");
    }
}

4. Pętla foreach (Dla każdego elementu kolekcji)

Pętla foreach jest bardzo wygodna, gdy chcemy przejść przez każdy element w kolekcji (np. liście tekstów, tablicy liczb itp.) bez konieczności ręcznego zarządzania indeksami. Poznamy ją lepiej, gdy wprowadzimy tablice i listy, ale warto o niej wspomnieć już teraz.

Schemat działania:

foreach (TYP_ELEMENTU NAZWA_ZMIENNEJ_ELEMENTU in KOLEKCJA)
{
    // Kod wewnątrz pętli - wykonuje się dla każdego elementu z KOLEKCJI
}

Przykład: Wyświetlanie imion

using System;

class Program
{
    static void Main()
    {
        string[] imiona = { "Ania", "Bartek", "Celina", "Dawid" }; // To jest prosta kolekcja (tablica) tekstów

        Console.WriteLine("Lista imion:");
        // Dla każdego 'imie' typu 'string' w kolekcji 'imiona'
        foreach (string imie in imiona)
        {
            Console.WriteLine($"- {imie}");
        }
    }
}

break i continue w pętlach (Sterowanie przepływem)

Czasem chcemy specjalnie sterować pętlą:

  • break;: Całkowicie przerywa działanie najbliższej pętli i kontynuuje wykonywanie kodu poza pętlą.
  • continue;: Przerywa tylko aktualne powtórzenie pętli i przechodzi do następnego powtórzenia.

Przykład:

using System;

class Program
{
    static void Main()
    {
        Console.WriteLine("Przykład z 'break':");
        for (int i = 1; i <= 10; i++)
        {
            if (i == 5)
            {
                Console.WriteLine("Znaleziono 5! Przerywam pętlę.");
                break; // Pętla zakończy się tutaj, nie wyświetli 6, 7, ...
            }
            Console.WriteLine(i);
        }
        Console.WriteLine("Pętla z 'break' zakończona.\n");

        Console.WriteLine("Przykład z 'continue':");
        for (int i = 1; i <= 5; i++)
        {
            if (i == 3)
            {
                Console.WriteLine("Pomiń liczbę 3.");
                continue; // Pomiń resztę kodu w tym powtórzeniu i przejdź do następnego 'i'
            }
            Console.WriteLine(i); // Nie wyświetli 3
        }
        Console.WriteLine("Pętla z 'continue' zakończona.");
    }
}

Podsumowanie Pętli:

PętlaKiedy używamy?Jak działa?
forGdy wiemy, ile razy chcemy powtórzyć operację.Inicjalizacja, warunek sprawdzany przed, krok po.
whileGdy powtarzamy, dopóki warunek jest prawdziwy. Nie wiemy, ile razy się wykona.Warunek sprawdzany przed każdym powtórzeniem.
do-whileGdy chcemy, aby kod wykonał się przynajmniej raz, a potem dopóki warunek jest prawdziwy.Kod wykonany raz, potem warunek sprawdzany po każdym powtórzeniu.
foreachGdy chcemy przejść przez każdy element w kolekcji (np. tablicy, liście).Automatycznie przechodzi przez wszystkie elementy.

Zadania na podsumowanie.

Teraz czas na praktykę z pętlami!

Zadanie 1 – Tabliczka mnożenia Napisz program, który:

  1. Prosi użytkownika o podanie liczby całkowitej (np. 7).
  2. Używając pętli for, wyświetla tabliczkę mnożenia dla tej liczby od 1 do 10.

Przykład działania (dla liczby 7):

Podaj liczbę dla tabliczki mnożenia: 7
7 x 1 = 7
7 x 2 = 14
...
7 x 10 = 70

Zadanie 2 – Sumowanie liczb Napisz program, który:

  1. Prosi użytkownika o podawanie liczb całkowitych.
  2. Program sumuje podane liczby.
  3. Pętla ma działać tak długo, aż użytkownik wpisze 0. Wtedy program ma zakończyć sumowanie i wyświetlić ostateczną sumę.
    • Wskazówka: Użyj pętli while lub do-while.

Przykład działania:

Podaj liczbę (0 kończy): 5
Podaj liczbę (0 kończy): 12
Podaj liczbę (0 kończy): -3
Podaj liczbę (0 kończy): 0
Suma wszystkich liczb wynosi: 14

Zadanie 3 – Sprawdzanie PIN-u Napisz program, który:

  1. Definiuje poprawny PIN jako zmienną string poprawnyPIN = "1234";.
  2. Daje użytkownikowi 3 próby na wpisanie poprawnego PIN-u.
  3. Jeśli użytkownik wpisze poprawny PIN, wyświetl „Dostęp przyznany!” i przerwij pętlę (break).
  4. Jeśli użytkownik wykorzysta wszystkie 3 próby i nie wpisze poprawnego PIN-u, wyświetl „Konto zablokowane!”.

Wskazówka: Użyj pętli for (do liczenia prób) i instrukcji if-else wewnątrz pętli.


Lekcja 7. Funkcje i Metody (Budowanie własnych narzędzi)

W dotychczasowych lekcjach pisaliśmy cały kod w jednej, głównej metodzie Main(). Nasze programy były proste, więc to wystarczało. Ale wyobraź sobie, co by było, gdyby program miał setki, a nawet tysiące linijek kodu! Robiłby się straszny bałagan, a odnalezienie i poprawienie czegoś byłoby koszmarem.

Na szczęście w programowaniu mamy funkcje (w C# częściej nazywane metodami), które pomagają nam organizować kod.

Wspólna logika dla konsoli, desktopu i MAUI
Dobrą praktyką jest umieścić obliczenia w osobnej klasie, np. public static class Obliczenia { ... }.
Potem:

MAUI wywołuje je z komend (ICommand) w ViewModelu.
Ten sam kod – trzy fronty. Konsola wywołuje metody bezpośrednio,
WinForms/WPF wywołuje je w obsłudze zdarzeń przycisków,

Co to jest funkcja/metoda?

Wyobraź sobie, że funkcja (metoda) to taki mały, wyspecjalizowany robot, który potrafi wykonać konkretne zadanie. Dajesz mu zadanie do wykonania (możesz mu coś przekazać, co jest potrzebne do pracy), a on to wykonuje i, ewentualnie, zwraca Ci wynik.

Na przykład:

  • Robot „DodajDwieLiczby”: dajesz mu 5 i 3, a on zwraca 8.
  • Robot „WyswietlPowitanie”: dajesz mu imię „Ania”, a on wyświetla „Witaj, Ania!”.

W programowaniu metoda to blok kodu, który wykonuje określone zadanie. Dzięki metodom:

  1. Organizujemy kod: Dzielimy duży program na mniejsze, łatwiejsze do zarządzania części.
  2. Unikamy powtarzania kodu: Raz napisaną metodę możemy wywoływać wiele razy w różnych miejscach programu.
  3. Ułatwiamy debugowanie: Łatwiej znaleźć błąd w małej metodzie niż w całym, długim kodzie.
  4. Może przyjmować dane wejściowe (tzw. parametry)
  5. Może zwracać wynik działania (przy pomocy słowa kluczowego return)
  6. Łatwia naprawę błędów.

Składnia metody w C#

Każda metoda ma swoją „wizytówkę”, która określa, jak się nazywa, co przyjmuje i co zwraca.

modyfikator_dostepu static typ_zwracany NazwaMetody(typ1 parametr1, typ2 parametr2, ...)
{
    // Ciało metody - tutaj piszemy kod, który wykonuje to zadanie
}

Rozłóżmy to na części:

  • modyfikator_dostepu: Na razie będziemy używać public (dostępna zewsząd) lub pominiemy go (domyślnie private). To bardziej zaawansowany temat związany z programowaniem obiektowym.
  • static: Na początku, wszystkie nasze metody będą static. Oznacza to, że metoda należy do klasy, a nie do konkretnego obiektu tej klasy. Dzięki temu możesz ją wywołać bezpośrednio, np. Program.Main() albo Console.WriteLine(). To też temat na przyszłość, ale na razie po prostu go używamy.
  • typ_zwracany: Określa, co metoda zwróci po zakończeniu swojej pracy.
    • Jeśli metoda nie zwraca żadnej wartości (np. tylko coś wyświetla), używamy słowa kluczowego void.
    • Jeśli metoda zwraca liczbę całkowitą, użyjemy int.
    • Jeśli zwraca tekst, użyjemy string.
    • Jeśli zwraca wartość logiczną, użyjemy bool.
    • …i tak dalej dla każdego typu danych.
  • NazwaMetody: To unikalna nazwa, którą sam nadajesz metodzie. Powinna być opisowa i mówić, co metoda robi (np. ObliczSume, WyswietlKomunikat).
    • Nazwy metod w C# zaczynają się dużą literą (konwencja PascalCase).
  • (typ1 parametr1, typ2 parametr2, ...): W nawiasach podajemy parametry (nazywane też argumentami). To są dane, które przekazujemy metodzie, żeby mogła wykonać swoje zadanie. Każdy parametr ma swój typ i nazwę.
    • Jeśli metoda nie potrzebuje żadnych danych wejściowych, nawiasy zostają puste: ().
  • { ... }: To jest ciało metody, czyli blok kodu, który zostanie wykonany, gdy metoda zostanie wywołana.

Metody bez parametrów i bez wartości zwracanej (void)

To najprostszy typ metody. Wykonuje jakieś zadanie, ale nie potrzebuje do tego dodatkowych danych i nie zwraca żadnego wyniku.

Przykład: Metoda, która wyświetla powitalny komunikat.

using System;

class Program
{
    // Definicja metody WyswietlPowitanie
    static void WyswietlPowitanie()
    {
        Console.WriteLine("Witaj w programie!");
        Console.WriteLine("Miło Cię widzieć.");
    }

    static void Main() // To jest nasza główna metoda, która zawsze się uruchamia
    {
        Console.WriteLine("Zaczynam program...");
        WyswietlPowitanie(); // WYWOŁANIE metody WyswietlPowitanie
        Console.WriteLine("Program zakończony.");
    }
}

Wyjaśnienie:

  1. Zdefiniowaliśmy nową metodę WyswietlPowitanie. Jest static void i nie ma parametrów.
  2. Wewnątrz Main() wywołaliśmy tę metodę po prostu pisząc jej nazwę i nawiasy ().
  3. Po wywołaniu, program „przeskoczył” do kodu w WyswietlPowitanie(), wykonał go, a potem wrócił do Main() i kontynuował od miejsca, w którym skończył.

Metody z parametrami, bez wartości zwracanej (void)

Takie metody potrzebują danych wejściowych, żeby wykonać swoje zadanie, ale same nie zwracają wyniku.

Przykład: Metoda, która wyświetla spersonalizowane powitanie.

using System;

class Program
{
    // Definicja metody WyswietlPowitanieImie
    // Przyjmuje jeden parametr typu string o nazwie 'imie'
    static void WyswietlPowitanieImie(string imie)
    {
        Console.WriteLine($"Witaj, {imie}!");
        Console.WriteLine("Cieszymy się, że jesteś z nami.");
    }

    static void Main()
    {
        Console.Write("Podaj swoje imię: ");
        string mojeImie = Console.ReadLine();

        WyswietlPowitanieImie(mojeImie); // Wywołanie metody, przekazanie zmiennej 'mojeImie' jako argumentu
        WyswietlPowitanieImie("Anna"); // Możemy też przekazać bezpośrednio tekst

        Console.WriteLine("Program zakończony.");
    }
}

Wyjaśnienie:

  1. Metoda WyswietlPowitanieImie ma jeden parametr: string imie. Oznacza to, że kiedy będziesz ją wywoływać, musisz jej podać jakiś tekst.
  2. Kiedy wywołujemy WyswietlPowitanieImie(mojeImie);, wartość zmiennej mojeImie (np. „Krzysztof”) jest kopiowana do parametru imie wewnątrz metody.
  3. Wewnątrz metody imie zachowuje się jak zwykła zmienna.

Metody z wartością zwracaną (nie-void)

Te metody wykonują swoje zadanie i po zakończeniu zwracają jakiś wynik. Muszą użyć słowa kluczowego return i po nim podać wartość zgodną z typ_zwracany metody.

Przykład: Metoda, która oblicza sumę dwóch liczb i zwraca wynik.

using System;

class Program
{
    // Definicja metody ObliczSume
    // Przyjmuje dwa parametry typu int: 'a' i 'b'
    // Zwraca wartość typu int
    static int ObliczSume(int a, int b)
    {
        int suma = a + b;
        return suma; // Zwraca wynik obliczeń
        // Kod poniżej 'return' w tej samej metodzie NIE zostanie wykonany
    }

    // Inna metoda, która zwraca string
    static string PobierzKomunikatPowitalny()
    {
        return "Witaj z funkcji!";
    }

    static void Main()
    {
        int liczba1 = 10;
        int liczba2 = 20;

        // Wywołujemy metodę ObliczSume i jej wynik (30) zapisujemy do zmiennej 'wynik'
        int wynik = ObliczSume(liczba1, liczba2);
        Console.WriteLine($"Suma liczb {liczba1} i {liczba2} wynosi: {wynik}"); // Wyświetli: 30

        // Możemy też od razu użyć wyniku metody
        Console.WriteLine($"Podwojona suma: {ObliczSume(5, 7) * 2}"); // (5+7)*2 = 24

        // Użycie metody zwracającej string
        string komunikat = PobierzKomunikatPowitalny();
        Console.WriteLine(komunikat);
    }
}

Wyjaśnienie:

  1. Metoda ObliczSume ma int jako typ_zwracany, co oznacza, że musi zwrócić liczbę całkowitą.
  2. Instrukcja return suma; powoduje, że wartość zmiennej suma jest „odesłana” z powrotem do miejsca, z którego metoda została wywołana.
  3. W Main(), gdy piszemy int wynik = ObliczSume(liczba1, liczba2);, to tak, jakbyśmy napisali int wynik = 30; po wykonaniu metody.

Przeciążanie metod (Overloading)

W C# możemy mieć kilka metod o tej samej nazwie, pod warunkiem, że mają różne listy parametrów. Oznacza to, że mogą przyjmować inną liczbę parametrów lub parametry różnych typów. Kompilator C# sam wybierze, której wersji metody użyć, na podstawie przekazanych argumentów.

Przykład: Metoda WyswietlDane, która potrafi wyświetlać dane na różne sposoby.

using System;

class Program
{
    // Metoda WyswietlDane: tylko imię
    static void WyswietlDane(string imie)
    {
        Console.WriteLine($"Witaj, {imie}!");
    }

    // Metoda WyswietlDane: imię i wiek
    static void WyswietlDane(string imie, int wiek)
    {
        Console.WriteLine($"Witaj, {imie}! Masz {wiek} lat.");
    }

    // Metoda WyswietlDane: wiek i wzrost (zauważ inna kolejność typów lub inna liczba typów)
    static void WyswietlDane(int wiek, double wzrost)
    {
        Console.WriteLine($"Osoba w wieku {wiek} lat ma {wzrost} cm wzrostu.");
    }

    static void Main()
    {
        WyswietlDane("Alicja"); // Wywoła pierwszą wersję
        WyswietlDane("Bartosz", 25); // Wywoła drugą wersję
        WyswietlDane(30, 180.5); // Wywoła trzecią wersję

        Console.WriteLine("Wszystkie dane wyświetlone.");
    }
}

Wyjaśnienie: Mimo że wszystkie metody nazywają się WyswietlDane, C# wie, którą z nich wywołać, patrząc na typy i liczbę argumentów, które do niej przekazujemy.

Po co jest przeciążenie metod?

Główny cel przeciążania metod to umożliwienie tworzenia bardziej elastycznych i intuicyjnych interfejsów dla twojego kodu. Wyobraź sobie to tak:

  1. Intuicyjność i spójność nazw: Zamiast wymyślać różne nazwy dla metod, które robią w zasadzie to samo, ale na trochę innych danych, możesz użyć tej samej, logicznej nazwy. Na przykład, chcesz wypisać coś na konsolę. Nie musisz pamiętać, czy to WriteLineString(), WriteLineInt(), WriteLineDouble(). Masz po prostu Console.WriteLine(), a C# sam wie, jak obsłużyć tekst, liczbę czy inną wartość, którą mu podasz. To sprawia, że API (interfejs programistyczny) jest prostsze do zapamiętania i użycia.
  2. Obsługa różnych typów danych: Czasem ta sama operacja musi być wykonana na różnych typach danych. Zamiast pisać ObliczPoleKwadratu(double bok) i ObliczPoleProstokata(double dlugosc, double szerokosc), możesz mieć dwie metody ObliczPole(), które C# rozróżni po parametrach.
  3. Obsługa różnej liczby argumentów: Może potrzebujesz, żeby metoda działała zarówno wtedy, gdy podasz jej wszystkie informacje, jak i wtedy, gdy podasz tylko część, a reszta zostanie uzupełniona domyślnymi wartościami.
  4. Zwiększona elastyczność: Przeciążone metody pozwalają twoim funkcjom „dostosować się” do kontekstu, w którym są wywoływane, bez konieczności tworzenia zupełnie nowych nazw.

Czy przeciążenie metod jest wskazane czy trzeba go unikać?

Jest zdecydowanie wskazane i jest to dobra praktyka programistyczna, pod warunkiem, że spełnia kluczowy warunek:

  • Przeciążone metody powinny wykonywać logicznie tę samą operację, nawet jeśli robią to na różnych danych wejściowych.

Kiedy jest wskazane (dobra praktyka):

  • Gdy metody wykonują to samo zadanie, ale na różnych typach danych.
    • int Dodaj(int a, int b)
    • double Dodaj(double a, double b)
    • string Dodaj(string s1, string s2) (konkatenacja)
    • Wszystkie „dodają” w jakimś sensie.
  • Gdy metody wykonują to samo zadanie, ale z różnym zestawem parametrów (np. jedne mają domyślne wartości).
    • void RysujKolo(int promien)
    • void RysujKolo(int promien, string kolor)
    • Obie metody rysują koło, ale druga daje więcej kontroli.
  • Dla przejrzystości i czytelności kodu. Ktoś, kto będzie używał twoich metod, łatwiej znajdzie funkcję, jeśli będzie miała spójną nazwę dla podobnych operacji.

Kiedy należy unikać (zła praktyka):

  • Gdy przeciążone metody wykonują zupełnie różne operacje, mimo tej samej nazwy. To prowadzi do zamieszania i błędów.
    • int Oblicz(int a, int b) (sumuje)
    • double Oblicz(double c, double d) (mnoży)
    • To jest zły przykład przeciążenia, ponieważ Oblicz w każdym przypadku robi coś innego.

Podsumowanie Funkcji i Metod:

Element metodyOpisPrzykłady
staticWskazuje, że metoda należy do klasy, nie do obiektu. Na razie używaj zawsze.static void MojaMetoda()
voidTyp zwracany. Oznacza, że metoda NIC NIE ZWRACA.static void Wyswietl()
typ_zwracanyTyp danych, które metoda zwróci (int, string, bool, double itd.)static int Oblicz()
NazwaMetodyUnikalna nazwa, którą sam nadajesz. Zaczyna się dużą literą.MojaFunkcja, ObliczPole
parametryDane, które przekazujemy metodzie do pracy. W nawiasach ().(int a, string b)
returnSłowo kluczowe do zwracania wartości z metody (tylko w metodach nie-void).return wynik;


Mini-most do MVVM (WPF/MAUI)
Jeśli wchodzisz w WPF/MAUI, rozważ prosty wzorzec MVVM:

  • ViewModel ma właściwości (Imie, Wynik) i komendy (PowitajCommand).
  • View (XAML) wiąże kontrolki z tymi właściwościami (Binding).
    Metody z tej lekcji idealnie nadają się do wywołań z komend.

Zadania na podsumowanie.

Zadanie 1 – Konwerter temperatur Napisz program, który zawiera dwie metody:

  1. static double CelsiusNaFahrenheit(double celsius):
    • Przyjmuje temperaturę w stopniach Celsjusza.
    • Oblicza temperaturę w stopniach Fahrenheita według wzoru: Fahrenheit = (Celsius * 9 / 5) + 32.
    • Zwraca wynik jako double.
  2. static double FahrenheitNaCelsius(double fahrenheit):
    • Przyjmuje temperaturę w stopniach Fahrenheita.
    • Oblicza temperaturę w stopniach Celsjusza według wzoru: Celsius = (Fahrenheit - 32) * 5 / 9.
    • Zwraca wynik jako double.

W metodzie Main():

  • Poproś użytkownika o podanie temperatury w Celsjuszach, przekonwertuj ją na Fahrenheity i wyświetl wynik.
  • Poproś użytkownika o podanie temperatury w Fahrenheitach, przekonwertuj ją na Celsjusze i wyświetl wynik.

Przykład działania:

Podaj temperaturę w stopniach Celsjusza: 25
25 Celsjusza to 77 Fahrenheita.

Podaj temperaturę w stopniach Fahrenheita: 68
68 Fahrenheita to 20 Celsjusza.

Zadanie 2 – Kalkulator pola figur Napisz program, który ma trzy przeciążone metody o nazwie ObliczPole:

  1. static double ObliczPole(double bok): Oblicza pole kwadratu (przyjmuje długość boku).
  2. static double ObliczPole(double dlugosc, double szerokosc): Oblicza pole prostokąta (przyjmuje długość i szerokość).
  3. static double ObliczPole(double podstawa, double wysokosc, string typFigury): Oblicza pole trójkąta (przyjmuje podstawę i wysokość, typFigury możesz zignorować lub użyć do wyświetlenia komunikatu).

W metodzie Main():

  • Wywołaj każdą z tych metod z różnymi argumentami i wyświetl ich wyniki, jasno określając, pole jakiej figury zostało obliczone.

Przykład działania:

Pole kwadratu o boku 5 to: 25
Pole prostokąta o bokach 4x6 to: 24
Pole trójkąta o podstawie 10 i wysokości 5 to: 25

Zadanie 3 – Sprawdzenie hasła Napisz metodę static bool SprawdzHaslo(string haslo):

  • Przyjmuje string jako hasło.
  • Zwraca true, jeśli hasło jest dłuższe niż 8 znaków i zawiera cyfrę (możesz uprościć to sprawdzenie, np. tylko sprawdzić długość), lubfalse w przeciwnym razie.
    • Wskazówka do cyfry: Możesz użyć prostego sprawdzania, np. haslo.Contains("1") || haslo.Contains("2") itp., albo pętli foreach po znakach hasła i char.IsDigit(znak). Dla uproszczenia na tym etapie wystarczy prosty warunek. *W metodzie Main():
  • Poproś użytkownika o wpisanie hasła.
  • Wywołaj metodę SprawdzHaslo i na podstawie jej wyniku wyświetl komunikat „Hasło poprawne!” lub „Hasło zbyt słabe!”.

Lekcja 8. Tablice i Kolekcje (Przechowywanie wielu danych)

Do tej pory nauczyliśmy się, jak przechowywać pojedyncze wartości w zmiennych. Ale co, jeśli chcemy przechowywać wiele wartości tego samego typu? Na przykład listę imion uczniów, wyniki egzaminów, czy temperatury z całego tygodnia? Do tego właśnie służą tablice i kolekcje. Pozwalają nam gromadzić i zarządzać grupami danych w jednym miejscu.


Część 1: Tablice jednowymiarowe (Lista danych)

Co to jest tablica?

Wyobraź sobie tablicę jako szereg ponumerowanych skrytek (pudełek) obok siebie, gdzie każda skrytka może przechowywać tylko jeden rodzaj rzeczy (np. tylko liczby, tylko teksty).

W programowaniu tablica to struktura danych, która przechowuje stałą liczbę elementów tego samego typu w kolejności. Każdy element ma swój unikalny indeks (numer pozycji), zaczynający się od 0.

Deklaracja i inicjalizacja tablicy

Zanim użyjemy tablicy, musimy ją zadeklarować (podać typ i nazwę) i zainicjalizować (stworzyć i ewentualnie wypełnić danymi).

1. Deklaracja (bez podawania rozmiaru od razu):

typ_elementow[] nazwaTablicy;

Przykład: int[] liczby; (deklarujemy, że liczby to będzie tablica liczb całkowitych)

2. Inicjalizacja (tworzenie tablicy i określanie jej rozmiaru):

  • Tworzenie pustej tablicy o określonym rozmiarze: C#nazwaTablicy = new typ_elementow[rozmiar]; Przykład: int[] liczby = new int[5]; (tworzy tablicę na 5 liczb całkowitych)
    • Po stworzeniu elementy tablicy są wypełniane domyślnymi wartościami dla danego typu (np. 0 dla int, null dla string, false dla bool).
  • Tworzenie i od razu wypełnianie elementami: C#typ_elementow[] nazwaTablicy = { wartosc1, wartosc2, wartosc3, ... }; Przykład: string[] imiona = { "Anna", "Bartek", "Celina" };
    • W tym przypadku rozmiar tablicy jest automatycznie określany przez liczbę podanych elementów.

Przykład tworzenia tablic:

using System;

class Program
{
    static void Main()
    {
        // Deklaracja i inicjalizacja tablicy na 3 temperatury (typ double)
        double[] temperatury = new double[3];

        // Domyślne wartości: 0, 0, 0
        Console.WriteLine($"Temperatura 1 (domyślna): {temperatury[0]}");

        // Deklaracja i inicjalizacja tablicy tekstów z od razu podanymi wartościami
        string[] dniTygodnia = { "Poniedziałek", "Wtorek", "Środa", "Czwartek", "Piątek", "Sobota", "Niedziela" };

        Console.WriteLine($"Pierwszy dzień tygodnia: {dniTygodnia[0]}"); // Zaczynamy od indeksu 0!
        Console.WriteLine($"Ostatni dzień tygodnia: {dniTygodnia[6]}"); // Dni od 0 do 6 to 7 elementów
    }
}

Dostęp do elementów tablicy

Do elementów tablicy odwołujemy się za pomocą ich indeksu (numeru pozycji) w nawiasach kwadratowych []. Pamiętaj, że indeks zawsze zaczyna się od 0!

  • tablica[indeks] – dostęp do elementu na danej pozycji.
  • tablica[indeks] = nowa_wartosc; – przypisanie nowej wartości do elementu.

Ważne: Próba dostępu do indeksu, który wykracza poza rozmiar tablicy (np. temperatury[5] w tablicy o rozmiarze 3), spowoduje błąd w trakcie działania programu (IndexOutOfRangeException).

Przykład dostępu i modyfikacji:

using System;

class Program
{
    static void Main()
    {
        int[] oceny = new int[4]; // Tablica na 4 oceny

        oceny[0] = 5; // Pierwsza ocena (na indeksie 0)
        oceny[1] = 4; // Druga ocena
        oceny[2] = 5; // Trzecia ocena
        oceny[3] = 3; // Czwarta ocena

        Console.WriteLine($"Pierwsza ocena: {oceny[0]}"); // Wyświetli 5
        Console.WriteLine($"Trzecia ocena: {oceny[2]}"); // Wyświetli 5

        oceny[1] = 2; // Zmieniamy drugą ocenę z 4 na 2
        Console.WriteLine($"Nowa druga ocena: {oceny[1]}"); // Wyświetli 2

        // Rozmiar tablicy możemy sprawdzić za pomocą właściwości Length
        Console.WriteLine($"Liczba ocen w tablicy: {oceny.Length}"); // Wyświetli 4
    }
}

Iterowanie (przechodzenie) przez tablicę

Aby przetworzyć wszystkie elementy w tablicy, najczęściej używamy pętli.

1. Pętla for (klasyczny sposób): Najlepsza, gdy potrzebujemy znać indeks elementu lub chcemy przejść przez część tablicy.

using System;

class Program
{
    static void Main()
    {
        string[] owoce = { "Jabłko", "Gruszka", "Śliwka", "Banan" };

        Console.WriteLine("Moje ulubione owoce:");
        // Pętla od indeksu 0 do (długość tablicy - 1)
        for (int i = 0; i < owoce.Length; i++)
        {
            Console.WriteLine($"- {owoce[i]} (indeks: {i})");
        }
    }
}

2. Pętla foreach (najprostszy sposób): Idealna, gdy chcemy po prostu przetworzyć każdy element po kolei, bez potrzeby odwoływania się do indeksów. Jest bardzo czytelna.

using System;

class Program
{
    static void Main()
    {
        int[] punkty = { 10, 25, 5, 30, 15 };
        int sumaPunktow = 0;

        Console.WriteLine("Otrzymane punkty:");
        foreach (int p in punkty) // Dla każdej liczby 'p' w tablicy 'punkty'
        {
            Console.WriteLine($"+ {p} punktów");
            sumaPunktow += p; // Dodajemy punkty do sumy
        }
        Console.WriteLine($"\nCałkowita suma punktów: {sumaPunktow}");
    }
}

ZADANIE:

Spróbuj rozwiązać te zadania, zanim przejdziesz do kolejnej części.

Zadanie 1 – Lista zakupów Napisz program, który:

  1. Tworzy tablicę typu string o nazwie listaZakupow, która pomieści 5 elementów.
  2. Poproś użytkownika o wpisanie 5 rzeczy, które chce kupić, i zapisz je kolejno do tablicy.
  3. Po zebraniu wszystkich pięciu elementów wyświetl całą listę zakupów, używając pętli for.

Zadanie 2 – Średnia temperatura Napisz program, który:

  1. Tworzy tablicę typu double o nazwie temperaturyTygodnia i inicjalizuje ją od razu siedmioma dowolnymi wartościami (temperaturami np. 18.5, 20.0, 19.2, ...).
  2. Oblicza sumę wszystkich temperatur.
  3. Oblicza średnią temperaturę.
  4. Wyświetla sumę i średnią temperaturę. Użyj pętli foreach do sumowania.

Część 2: Tablice dwuwymiarowe i Kolekcje (listy i słowniki)

1. Tablice dwuwymiarowe (Tabele danych)

Tablica dwuwymiarowa to nic innego jak tablica tablic, czyli struktura do przechowywania danych w formie tabeli (wiersze i kolumny). Przydaje się, np. do przechowywania współrzędnych, plansz do gier (szachy, statki), czy macierzy.

Schemat działania:

typ_elementow[,] nazwaTablicy; // Zauważ przecinek w nawiasach kwadratowych!

Deklaracja i inicjalizacja:

typ_elementow[,] nazwaTablicy = new typ_elementow[liczba_wierszy, liczba_kolumn];

Przykład: int[,] macierz = new int[3, 3]; (tworzy macierz 3×3)

Dostęp do elementów: Odwołujemy się do elementów za pomocą dwóch indeksów: [numer_wiersza, numer_kolumny]. Pamiętaj, że oba indeksy zaczynają się od 0.

Przykład:

using System;

class Program
{
    static void Main()
    {
        // Tablica dwuwymiarowa 3x3 na liczby całkowite
        int[,] plansza = new int[3, 3];

        // Wypełnianie planszy danymi (ręcznie dla przykładu)
        plansza[0, 0] = 1; // Wiersz 0, Kolumna 0
        plansza[0, 1] = 2;
        plansza[0, 2] = 3;

        plansza[1, 0] = 4;
        plansza[1, 1] = 5; // Środkowy element
        plansza[1, 2] = 6;

        plansza[2, 0] = 7;
        plansza[2, 1] = 8;
        plansza[2, 2] = 9;

        // Dostęp do elementu
        Console.WriteLine($"Element środkowy: {plansza[1, 1]}"); // Wyświetli 5

        // Iterowanie przez tablicę dwuwymiarową (zagnieżdżone pętle for)
        Console.WriteLine("\nZawartość planszy:");
        for (int wiersz = 0; wiersz < 3; wiersz++) // Pętla dla wierszy
        {
            for (int kolumna = 0; kolumna < 3; kolumna++) // Pętla dla kolumn
            {
                Console.Write($"{plansza[wiersz, kolumna]} "); // Wyświetl element
            }
            Console.WriteLine(); // Po każdym wierszu przejdź do nowej linii
        }
    }
}

2. Kolekcje (Bardziej elastyczne listy i słowniki)

Tablice są proste, ale mają stały rozmiar. Jeśli chcemy dynamicznie dodawać lub usuwać elementy, potrzebujemy kolekcji. To bardziej zaawansowane struktury danych, które są częścią przestrzeni nazw System.Collections.Generic. Są jak odpowiedniki list i słowników z Pythona.

Aby ich używać, musisz pamiętać o dodaniu using System.Collections.Generic; na początku pliku.

a) List<T> (Dynamiczne listy)

List<T> (gdzie T oznacza dowolny typ danych, np. List<string>, List<int>) to elastyczny odpowiednik tablicy, która może zmieniać swój rozmiar. Jest to najczęściej używana kolekcja w C# i odpowiada listom z Pythona.

Tworzenie listy:

List<typ_elementow> nazwaListy = new List<typ_elementow>();

Przykład: List<string> imiona = new List<string>();

Podstawowe operacje:

  • Add(element): Dodaje element na koniec listy.
  • Remove(element): Usuwa pierwsze wystąpienie podanego elementu.
  • RemoveAt(index): Usuwa element z podanego indeksu.
  • Insert(index, element): Wstawia element na określoną pozycję.
  • [index]: Dostęp do elementu za pomocą indeksu (jak w tablicy).
  • Count: Właściwość zwracająca aktualną liczbę elementów w liście (odpowiednik Length dla tablic).
  • Contains(element): Sprawdza, czy lista zawiera dany element (zwraca bool).

Przykład:

using System;
using System.Collections.Generic; // WAŻNE! Musisz dodać to 'using'

class Program
{
    static void Main()
    {
        List<string> listaStudentow = new List<string>(); // Pusta lista stringów

        Console.WriteLine($"Początkowa liczba studentów: {listaStudentow.Count}"); // 0

        listaStudentow.Add("Alicja");
        listaStudentow.Add("Bartosz");
        listaStudentow.Add("Cezary");

        Console.WriteLine($"Po dodaniu: {listaStudentow.Count} studentów"); // 3

        Console.WriteLine($"Pierwszy student: {listaStudentow[0]}"); // Alicja

        listaStudentow.Remove("Bartosz"); // Usuwamy Bartosza
        Console.WriteLine($"Po usunięciu Bartosza: {listaStudentow.Count} studentów"); // 2

        listaStudentow.Insert(0, "Dorota"); // Dodajemy Dorotę na początku (indeks 0)
        Console.WriteLine($"Po dodaniu Doroty: {listaStudentow[0]}"); // Dorota

        // Iterowanie przez listę za pomocą foreach (najczęściej)
        Console.WriteLine("\nAktualna lista studentów:");
        foreach (string student in listaStudentow)
        {
            Console.WriteLine($"- {student}");
        }

        bool czyJestAlicja = listaStudentow.Contains("Alicja");
        Console.WriteLine($"\nCzy na liście jest Alicja? {czyJestAlicja}"); // True
    }
}

b) Dictionary<TKey, TValue> (Słowniki/Mapy)

Dictionary<TKey, TValue> to kolekcja, która przechowuje dane w parach klucz-wartość (key-value pairs). Każdy klucz (TKey) musi być unikalny, a za jego pomocą możemy szybko odnaleźć odpowiadającą mu wartość (TValue). To bezpośredni odpowiednik słownika z Pythona.

Tworzenie słownika:

Dictionary<typ_klucza, typ_wartosci> nazwaSlownika = new Dictionary<typ_klucza, typ_wartosci>();

Przykład: Dictionary<string, int> wiekUczniow = new Dictionary<string, int>();

Podstawowe operacje:

  • Add(klucz, wartosc): Dodaje nową parę klucz-wartość.
  • [klucz] = wartosc: Używane do dodawania nowej pary lub modyfikacji istniejącej wartości dla danego klucza.
  • [klucz]: Dostęp do wartości za pomocą klucza.
  • Remove(klucz): Usuwa parę po kluczu.
  • ContainsKey(klucz): Sprawdza, czy słownik zawiera dany klucz (zwraca bool).
  • Count: Właściwość zwracająca liczbę par w słowniku.

Przykład:

using System;
using System.Collections.Generic; // WAŻNE! Musisz dodać to 'using'

class Program
{
    static void Main()
    {
        // Słownik przechowujący wiek (int) dla imion (string)
        Dictionary<string, int> wiekOsob = new Dictionary<string, int>();

        wiekOsob.Add("Jan", 30);
        wiekOsob.Add("Anna", 25);
        wiekOsob["Piotr"] = 40; // Inny sposób dodawania

        Console.WriteLine($"Wiek Anny: {wiekOsob["Anna"]} lat"); // Dostęp do wartości po kluczu

        wiekOsob["Jan"] = 31; // Zmiana wieku Jana
        Console.WriteLine($"Nowy wiek Jana: {wiekOsob["Jan"]} lat"); // 31

        Console.WriteLine($"\nLiczba osób w słowniku: {wiekOsob.Count}"); // 3

        if (wiekOsob.ContainsKey("Kasia"))
        {
            Console.WriteLine("Kasia jest w słowniku.");
        }
        else
        {
            Console.WriteLine("Kasia nie jest w słowniku.");
        }

        // Iterowanie przez słownik (foreach)
        Console.WriteLine("\nLista osób i ich wieku:");
        foreach (var para in wiekOsob) // 'var' pozwoli C# samodzielnie określić typ (KeyValuePair<string, int>)
        {
            Console.WriteLine($"- {para.Key}: {para.Value} lat");
        }

        wiekOsob.Remove("Anna"); // Usuwamy Annę
        Console.WriteLine($"\nPo usunięciu Anny, liczba osób: {wiekOsob.Count}"); // 2
    }
}

3. Krotki (Tuples)

Krotki w C# to struktury danych, które pozwalają grupować ze sobą wartości o różnych typach. Są lekkie i przydatne, gdy chcemy zwrócić z funkcji kilka wartości naraz, bez tworzenia osobnej klasy. Podobnie jak w Pythonie, wartości w krotce są niezmienne po utworzeniu.

Tworzenie krotki:

(typ1 nazwa1, typ2 nazwa2, ...) nazwaKrotki = (wartosc1, wartosc2, ...);

Możesz też użyć słowa var i pozwolić C# wykryć typy:

var nazwaKrotki = (wartosc1, wartosc2, ...);

Dostęp do elementów krotki: Możesz uzyskać dostęp do elementów krotki za pomocą nadanych im nazw (nazwaKrotki.nazwa1) lub za pomocą domyślnych nazw: Item1, Item2 itd.

Przykład:

using System;

class Program
{
    static void Main()
    {
        // Krotka przechowująca dane o studencie: imię (string), wiek (int), średnia (double)
        (string imie, int wiek, double srednia) student = ("Karol", 20, 4.5);

        Console.WriteLine($"Student: {student.imie}, wiek: {student.wiek}, średnia: {student.srednia}");

        // Można też użyć domyślnych nazw, jeśli nie nadaliśmy własnych
        var samochod = ("Toyota", 2020, 15000.0); // Krotka bez nadanych nazw elementów
        Console.WriteLine($"Samochód: {samochod.Item1}, rok: {samochod.Item2}, cena: {samochod.Item3}");

        // Krotka jako wynik funkcji (później, gdy poznasz funkcje)
        var wynikObliczen = ObliczCos(10, 5);
        Console.WriteLine($"Suma: {wynikObliczen.suma}, Iloczyn: {wynikObliczen.iloczyn}");
    }

    // Funkcja zwracająca krotkę
    static (int suma, int iloczyn) ObliczCos(int a, int b)
    {
        return (a + b, a * b);
    }
}

Podsumowanie Tablic i Kolekcji:

Struktura danychKiedy używamy?Kluczowe cechyOdpowiednik w Pythonie
Tablica []Stała liczba elementów tego samego typu, znana z góry.Rozmiar stały, indeksowanie od 0, szybki dostęp do elementu.(podobne do list, ale o stałym rozmiarze)
List<T>Dynamiczna lista elementów tego samego typu.Rozmiar zmienny, łatwe dodawanie/usuwanie, indeksowanie od 0.Lista (list)
Dictionary<TKey, TValue>Dane w parach klucz-wartość.Klucze unikalne, szybkie wyszukiwanie po kluczu.Słownik (dict)
Krotka ()Grupowanie małej liczby wartości RÓŻNYCH typów.Niezmienna, lekka, używana do zwracania wielu wartości.Krotka (tuple)

Zadania na podsumowanie.

Zadanie 1 – Lista ulubionych filmów Napisz program, który:

  1. Tworzy listę (List<string>) o nazwie ulubioneFilmy.
  2. Dodaje do niej co najmniej 3 tytuły ulubionych filmów.
  3. Wyświetla wszystkie filmy z listy, używając pętli foreach.
  4. Pyta użytkownika, czy chce usunąć jakiś film z listy (po nazwie). Jeśli film istnieje, usuń go i wyświetl nową listę. Jeśli nie, wyświetl komunikat, że filmu nie ma.
  5. Na koniec wyświetl, ile filmów pozostało na liście.

Zadanie 2 – Dane kontaktowe (Słownik) Napisz program, który:

  1. Tworzy słownik (Dictionary<string, string>) o nazwie kontakty, gdzie kluczem będzie imię, a wartością numer telefonu.
  2. Dodaje do słownika co najmniej 3 przykładowe kontakty (imię i numer).
  3. Pyta użytkownika o imię osoby, której numer telefonu chce sprawdzić.
  4. Jeśli imię istnieje w słowniku, wyświetl numer telefonu tej osoby.
  5. Jeśli imienia nie ma w słowniku, wyświetl komunikat „Brak kontaktu dla tej osoby.”

Zadanie 3 – Dane osoby (Krotka) Napisz program, który:

  1. Definiuje krotkę o nazwie osoba zawierającą:
    • string imię,
    • string nazwisko,
    • int wiek.
  2. Przypisz do tej krotki dane jakiejś osoby (np. Twoje).
  3. Wyświetl dane osoby z krotki w czytelnym formacie, np. „Imię: [imię], Nazwisko: [nazwisko], Wiek: [wiek]”.

Lekcja 9. Praca z Plikami (Odczyt i Zapis danych)

Do tej pory nasze programy działały „na żywo”: dane, które podał użytkownik, były używane, a po zakończeniu programu… znikały. Jeśli chcielibyśmy, żeby program pamiętał coś na przyszłość – np. listę zakupów, wysoki wynik w grze, czy ustawienia – musimy zapisać te dane do pliku.

Praca z plikami pozwala naszym programom komunikować się ze światem zewnętrznym i przechowywać informacje na stałe na dysku komputera.

Co to jest plik?

Wyobraź sobie plik jako zeszyt lub segregator na komputerze. Możesz w nim zapisywać informacje (tekst, liczby, obrazy – cokolwiek) i odczytywać je później. Każdy plik ma swoją nazwę i lokalizację na dysku.

W C# (i w ogóle w programowaniu) najczęściej pracujemy z plikami tekstowymi. Poznaliśmy już podstawowy format TXT, ale istnieje też inny, niezwykle popularny format: JSON.

Przestrzeń nazw System.IO

Aby móc pracować z plikami w C#, musimy dodać na początku naszego pliku instrukcję using:

using System.IO; // Ta przestrzeń nazw zawiera narzędzia do pracy z plikami i folderami

Ta linijka daje nam dostęp do klas takich jak File, StreamReader, StreamWriter, które służą do operacji na plikach.


1. Zapisywanie danych do pliku TXT (Pisanie)

Najprostszym sposobem na zapisywanie tekstu do pliku jest użycie klasy File i jej metody WriteAllText().

Schemat działania:

File.WriteAllText("sciezka/do/pliku.txt", "Tekst, który chcemy zapisać.");
  • File.WriteAllText(): Ta metoda statyczna z klasy File służy do zapisania całego podanego tekstu do pliku.
    • Pierwszy argument: Ścieżka do pliku (nazwa pliku). Jeśli podasz tylko nazwę pliku ("mojplik.txt"), zostanie on utworzony w tym samym katalogu, co uruchomiony program. Możesz też podać pełną ścieżkę ("C:/MojeDokumenty/dane.txt").
    • Drugi argument: Tekst, który ma zostać zapisany do pliku.

Ważne: Jeśli plik o podanej nazwie już istnieje, metoda WriteAllText() nadpisze (skasuje i zastąpi) jego zawartość nowym tekstem!

Przykład: Zapisywanie prostego tekstu

using System;
using System.IO; // Pamiętaj o tej linijce!

class Program
{
    static void Main()
    {
        string nazwaPlikuTxt = "moja_wiadomosc.txt";
        string tekstDoZapisu = "Witaj, świecie plikowy!\nTo jest kolejna linia tekstu.";

        try // Zawsze warto używać try-catch przy operacjach na plikach!
        {
            File.WriteAllText(nazwaPlikuTxt, tekstDoZapisu);
            Console.WriteLine($"Tekst został zapisany do pliku: {nazwaPlikuTxt}");
        }
        catch (Exception ex) // Obsługa błędów, np. brak dostępu do pliku
        {
            Console.WriteLine($"Wystąpił błąd podczas zapisu: {ex.Message}");
        }
    }
}

Po uruchomieniu tego kodu, w katalogu obok pliku .exe twojego programu pojawi się plik moja_wiadomosc.txt z zapisanym tekstem.


2. Odczytywanie danych z pliku TXT (Czytanie)

Aby odczytać całą zawartość pliku tekstowego, również możemy użyć klasy File, a konkretnie metody ReadAllText().

Schemat działania:

string zawartosc = File.ReadAllText("sciezka/do/pliku.txt");
  • File.ReadAllText(): Ta metoda statyczna odczytuje całą zawartość pliku i zwraca ją jako jeden długi string.
    • Argument: Ścieżka do pliku, który chcemy odczytać.

Ważne: Jeśli plik o podanej nazwie nie istnieje, program zgłosi błąd (FileNotFoundException)!

Przykład: Odczytywanie zapisanego tekstu

using System;
using System.IO; // Pamiętaj o tej linijce!

class Program
{
    static void Main()
    {
        string nazwaPlikuTxt = "moja_wiadomosc.txt"; // Plik, który zapisaliśmy wcześniej

        try
        {
            string odczytanyTekst = File.ReadAllText(nazwaPlikuTxt);
            Console.WriteLine($"\n--- Zawartość pliku {nazwaPlikuTxt} ---");
            Console.WriteLine(odczytanyTekst);
            Console.WriteLine("---------------------------------\n");
        }
        catch (FileNotFoundException) // Konkretny błąd, gdy plik nie istnieje
        {
            Console.WriteLine($"Błąd: Plik '{nazwaPlikuTxt}' nie został znaleziony.");
        }
        catch (Exception ex) // Inne błędy
        {
            Console.WriteLine($"Wystąpił błąd podczas odczytu: {ex.Message}");
        }
    }
}

Wskazówka: Dobrą praktyką jest umieszczanie operacji na plikach w bloku try-catch, aby bezpiecznie obsłużyć ewentualne błędy, takie jak brak pliku, brak uprawnień do zapisu/odczytu itp.


3. Dopisanie danych do pliku TXT (Dodawanie)

Co, jeśli chcemy dodać nowy tekst na koniec istniejącego pliku, zamiast nadpisywać całą jego zawartość? Używamy metody AppendAllText().

Schemat działania:

File.AppendAllText("sciezka/do/pliku.txt", "Tekst do dopisania.");
  • File.AppendAllText(): Dopisuje podany tekst na koniec pliku.
    • Jeśli plik nie istnieje, zostanie utworzony.
    • Jeśli plik istnieje, tekst zostanie dodany na jego końcu.

Przykład: Dopisanie nowej linii do pliku logu

using System;
using System.IO;

class Program
{
    static void Main()
    {
        string plikLogu = "log.txt";
        string nowaWiadomosc = $"{DateTime.Now}: Program uruchomiony ponownie.\n"; // Dodajemy datę i czas

        try
        {
            File.AppendAllText(plikLogu, nowaWiadomosc);
            Console.WriteLine($"Wiadomość została dopisana do pliku: {plikLogu}");

            // Możemy od razu sprawdzić zawartość
            Console.WriteLine("\n--- Aktualna zawartość log.txt ---");
            Console.WriteLine(File.ReadAllText(plikLogu));
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Wystąpił błąd: {ex.Message}");
        }
    }
}

4. Praca z plikami TXT linia po linii (Listy tekstów)

Często wygodniej jest odczytywać lub zapisywać pliki tekstowe jako listę linii, a nie jeden długi string.

  • File.ReadAllLines("sciezka.txt"): Odczytuje wszystkie linie z pliku i zwraca je jako tablicę typu string[], gdzie każdy element tablicy to jedna linia z pliku.
  • File.WriteAllLines("sciezka.txt", string[] linie): Zapisuje każdą linię z podanej tablicy string do pliku.

Przykład: Lista zadań do zrobienia

using System;
using System.IO;
using System.Collections.Generic; // Potrzebne do List<string>

class Program
{
    static void Main()
    {
        string plikZadan = "zadania.txt";

        // ---------------- ZAPISYWANIE ZADAŃ ----------------
        List<string> listaZadan = new List<string>
        {
            "Kupić mleko",
            "Zadzwonić do babci",
            "Napisać kod C#",
            "Poczytać książkę"
        };

        try
        {
            // Konwertujemy List<string> na string[] dla WriteAllLines
            File.WriteAllLines(plikZadan, listaZadan.ToArray());
            Console.WriteLine($"Zadania zapisane do pliku: {plikZadan}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Błąd podczas zapisu zadań: {ex.Message}");
        }

        // ---------------- ODCZYTYWANIE ZADAŃ ----------------
        try
        {
            Console.WriteLine($"\n--- Twoje zadania z pliku {plikZadan} ---");
            string[] odczytaneZadania = File.ReadAllLines(plikZadan); // Odczytujemy jako tablicę linii

            foreach (string zadanie in odczytaneZadania)
            {
                Console.WriteLine($"- {zadanie}");
            }
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine($"Błąd: Plik zadań '{plikZadan}' nie został znaleziony.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Błąd podczas odczytu zadań: {ex.Message}");
        }
    }
}

5. Praca z plikami JSON (Dane strukturalne)

Pliki JSON (JavaScript Object Notation) to tekstowy format wymiany danych, bardzo popularny w webowych aplikacjach i wszędzie tam, gdzie trzeba przesyłać lub przechowywać dane w ustrukturyzowany sposób. Zamiast prostego tekstu, JSON pozwala na reprezentowanie obiektów (jak słowniki) i list (jak tablice).

Dlaczego JSON jest popularny?

  • Łatwy do czytania dla ludzi.
  • Łatwy do parsowania (przetwarzania) przez maszyny.
  • Powszechnie używany w internecie (API).

Podstawowe typy danych w JSON:

  • Obiekt (odpowiednik słownika/obiektu w C#): { "klucz": "wartość", "innyKlucz": 123 }
  • Tablica (odpowiednik listy/tablicy w C#): [ "element1", "element2", 3 ]
  • Wartości: stringi (w cudzysłowach), liczby, booleany (true/false), null.

Obsługa JSON w C# (System.Text.Json)

Aby pracować z JSON w C#, będziemy potrzebować biblioteki System.Text.Json. Jest ona częścią .NET (od .NET Core 3.0 i .NET 5+), więc nie musisz instalować nic dodatkowego, wystarczy dodać using:

using System.Text.Json; // Ważne!

Kluczowe operacje:

  • Serializacja: Zamiana obiektu C# na tekst JSON (zapis do pliku).
  • Deserializacja: Zamiana tekstu JSON na obiekt C# (odczyt z pliku).

Aby to zademonstrować, stwórzmy prostą klasę C#, która będzie odpowiadała strukturze danych w JSON.

// Ta klasa reprezentuje "osobę"
public class Osoba
{
    public string Imie { get; set; } // Właściwość na imię
    public int Wiek { get; set; }     // Właściwość na wiek
    public bool JestStudentem { get; set; } // Właściwość na status studenta
}

a) Zapisywanie obiektu do pliku JSON (Serializacja)

using System;
using System.IO;
using System.Text.Json; // Dodaj to using!

class Program
{
    static void Main()
    {
        // 1. Tworzymy obiekt C#
        Osoba nowaOsoba = new Osoba
        {
            Imie = "Anna Kowalska",
            Wiek = 28,
            JestStudentem = false
        };

        // 2. Konwertujemy obiekt C# na tekst JSON (serializacja)
        // Opcja 'WriteIndented = true' sprawia, że JSON jest ładnie sformatowany (z wcięciami)
        var options = new JsonSerializerOptions { WriteIndented = true };
        string jsonString = JsonSerializer.Serialize(nowaOsoba, options);

        // 3. Zapisujemy tekst JSON do pliku
        string nazwaPlikuJson = "osoba.json";
        try
        {
            File.WriteAllText(nazwaPlikuJson, jsonString);
            Console.WriteLine($"Obiekt zapisano do pliku: {nazwaPlikuJson}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Błąd podczas zapisu JSON: {ex.Message}");
        }
    }
}

// Musisz zdefiniować tę klasę gdzieś w swoim pliku (np. na samym dole)
public class Osoba
{
    public string Imie { get; set; }
    public int Wiek { get; set; }
    public bool JestStudentem { get; set; }
}

Po uruchomieniu tego kodu, w pliku osoba.json zobaczysz coś takiego:

JSON

{
  "Imie": "Anna Kowalska",
  "Wiek": 28,
  "JestStudentem": false
}

b) Odczytywanie obiektu z pliku JSON (Deserializacja)

C#

using System;
using System.IO;
using System.Text.Json; // Dodaj to using!

class Program
{
    static void Main()
    {
        string nazwaPlikuJson = "osoba.json"; // Plik, który zapisaliśmy wcześniej

        try
        {
            // 1. Odczytujemy cały tekst z pliku JSON
            string jsonString = File.ReadAllText(nazwaPlikuJson);

            // 2. Konwertujemy tekst JSON na obiekt C# (deserializacja)
            Osoba odczytanaOsoba = JsonSerializer.Deserialize<Osoba>(jsonString);

            // 3. Używamy danych z obiektu C#
            Console.WriteLine($"\n--- Dane odczytane z pliku {nazwaPlikuJson} ---");
            Console.WriteLine($"Imię: {odczytanaOsoba.Imie}");
            Console.WriteLine($"Wiek: {odczytanaOsoba.Wiek}");
            Console.WriteLine($"Student: {odczytanaOsoba.JestStudentem}");
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine($"Błąd: Plik '{nazwaPlikuJson}' nie został znaleziony.");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Wystąpił błąd podczas odczytu/deserializacji JSON: {ex.Message}");
        }
    }
}

// Ta klasa musi być zdefiniowana, aby działała deserializacja!
public class Osoba
{
    public string Imie { get; set; }
    public int Wiek { get; set; }
    public bool JestStudentem { get; set; }
}

Podsumowanie Pracy z Plikami:

Format plikuKiedy używamy?Metody / Biblioteki
TXTProsty, nieskomplikowany tekst; logi; proste listy.System.IO.File.WriteAllText/ReadAllText System.IO.File.AppendAllText/ReadAllLines
JSONDane strukturalne (obiekty, listy obiektów); konfiguracje; wymiana danych z API.System.Text.Json.JsonSerializer.Serialize/Deserialize

Zadania na podsumowanie.

Zadanie 1 – Dziennik pogody (rozszerzony) Napisz program, który:

  1. Poprosi użytkownika o podanie aktualnej temperatury (np. 22.5).
  2. Poprosi o krótki opis pogody (np. „Słonecznie”).
  3. Zapisze te dane do pliku o nazwie pogoda.txt. Każdy wpis powinien być w nowej linii i zawierać datę i czas, temperaturę i opis.
    • Wskazówka: Użyj DateTime.Now.ToString() dla daty i czasu, a do zapisu File.AppendAllText(), aby kolejne wpisy były dopisywane, a nie nadpisywane.
    • Format linii: [Data i Czas] Temperatura: [temperatura]°C, Opis: [opis]

Zadanie 2 – Lista ulubionych dań (JSON) Napisz program, który:

  1. Zdefiniuje prostą klasę np. public class Danie { public string Nazwa { get; set; } public int Kalorie { get; set; } }.
  2. Stworzy listę obiektów List<Danie> (np. dwa-trzy dania z nazwami i kaloriami).
  3. Zapisze (serializuje) tę listę do pliku JSON o nazwie ulubione_dania.json.
  4. Następnie odczyta (deserializuje) tę listę z pliku JSON.
  5. Wyświetli na konsoli nazwy i kalorie wszystkich odczytanych dań.

Przykład pliku JSON, który powinien powstać:

[
  {
    "Nazwa": "Pizza",
    "Kalorie": 800
  },
  {
    "Nazwa": "Sałatka Cezar",
    "Kalorie": 450
  }
]
  • Wskazówka: Do serializacji/deserializacji listy użyj:
    • string jsonString = JsonSerializer.Serialize(listaDan, options);
    • List<Danie> odczytaneDania = JsonSerializer.Deserialize<List<Danie>>(jsonString);

Zadanie 3 – Ustawienia programu (JSON) Napisz program, który:

  1. Zdefiniuje klasę public class Ustawienia { public string NazwaUzytkownika { get; set; } public bool TrybCiemny { get; set; } }.
  2. Stworzy obiekt Ustawienia z przykładowymi danymi.
  3. Zapisze ten obiekt do pliku ustawienia.json.
  4. Odczyta obiekt Ustawienia z pliku.
  5. Wyświetli odczytane ustawienia na konsoli.