Część 1. Aplikacja konsolowa
Błąd 1: Brak metody zwracającej największą liczbę w systemie dziesiętnym
To jeden z najdroższych błędów punktowo. Spotykałem szkielety metod, które wyglądały tak:
csharp
public long GetLargestNumber()
{
return 0; // TODO: dokończyć
}
…i tyle. Metoda jest, technicznie istnieje, ale nie liczy nic. Egzaminator widzi return 0; i punktów nie ma.
Co trzeba zrobić: po zamianie symboli na cyfry trójkowe każdy wiersz tablicy to liczba w systemie trójkowym (np. "0121"). Trzeba ją przekonwertować na dziesiętną i znaleźć największą.
Liczba 0121 w systemie trójkowym to:
0 × 3³ + 1 × 3² + 2 × 3¹ + 1 × 3⁰ = 0 + 9 + 6 + 1 = 16
W C# konwersję załatwia jedna linia: Convert.ToInt64(napis, 3) — drugi argument to podstawa systemu liczbowego.
csharp
public long GetLargestNumber()
{
long max = 0;
foreach (string row in tablica)
{
long value = Convert.ToInt64(row, 3); // 3 = system trójkowy
if (value > max)
{
max = value;
}
}
return max;
}
Pamiętaj o kolejności wywołań w Main. Tę metodę wywołujesz po metodzie zamieniającej symbole na cyfry. Jeśli odpalisz ją wcześniej, Convert.ToInt64 rzuci wyjątkiem — bo +, o, * to nie cyfry.
Błąd 2: Brak klasy z prywatnym polem tablicowym i konstruktorem
Arkusz wprost wymaga osobnej klasy. Cały kod w Main() nie wystarczy — nawet jeśli liczy dobrze. Muszą być trzy rzeczy:
- prywatne pole z tablicą stringów,
- konstruktor przyjmujący tablicę i przypisujący ją do pola,
- metody operujące na polu.
csharp
class SymbolConverter
{
// Prywatne pole - dostępne TYLKO w tej klasie,
// niedostępne nawet dla klas potomnych
private string[] tablica;
// Konstruktor - przyjmuje tablicę i przypisuje do pola
public SymbolConverter(string[] dane)
{
tablica = dane;
}
// Metoda zamieniająca symbole na cyfry i wypisująca wynik
public void ConvertAndPrint()
{
for (int i = 0; i < tablica.Length; i++)
{
tablica[i] = tablica[i]
.Replace('+', '0')
.Replace('o', '1')
.Replace('*', '2');
Console.WriteLine(tablica[i]);
}
}
// Metoda zwracająca największą liczbę w systemie dziesiętnym
public long GetLargestNumber()
{
long max = 0;
foreach (string row in tablica)
{
long value = Convert.ToInt64(row, 3);
if (value > max) max = value;
}
return max;
}
}
Dwie rzeczy, na które zwróć szczególną uwagę:
- Słowo
privatemusi tam być.publicalbo brak modyfikatora to nie to samo — arkusz wymaga, żeby pole było niedostępne nawet dla klas potomnych, a to zapewnia tylkoprivate. - Konstruktor musi mieć dokładnie tę samą nazwę co klasa (
SymbolConverter). Bez tego to nie konstruktor, tylko zwykła metoda.
Błąd 3: Odczyt symbole.txt po ścieżce absolutnej
To jest błąd, który zabija program na stanowisku egzaminacyjnym. Wyobraź sobie:
csharp
// ŹLE
string[] lines = File.ReadAllLines(@"C:\Users\Jan\Desktop\symbole.txt");
Na Twoim komputerze działa. Egzaminator otwiera projekt na swoim komputerze — folder C:\Users\Jan\Desktop\ nie istnieje, program się wywala. Zero punktów za uruchomienie.
Opcja 1 (najprostsza): plik symbole.txt w folderze projektu, w kodzie sama nazwa pliku:
csharp
// DOBRZE
string[] lines = File.ReadAllLines("symbole.txt");
W Visual Studio: kliknij prawym na plik symbole.txt w Solution Explorer → Properties → ustaw Copy to Output Directory na Copy always. Plik trafi obok .exe przy każdej kompilacji.
Opcja 2 (jeszcze bezpieczniejsza): ścieżka względna do folderu z programem:
csharp
string sciezka = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "symbole.txt");
string[] lines = File.ReadAllLines(sciezka);
Ta wersja działa nawet jeśli ktoś uruchomi .exe z innego katalogu.
Kompletny program konsolowy z dokumentacją
Pokazuję od razu z komentarzami nagłówkowymi w stylu z Listingu 1 — to się przyda przy Błędzie 7.
csharp
using System;
using System.IO;
namespace KonsolaSymbole
{
/*
**********************************************
nazwa klasy: SymbolConverter
opis klasy: Przechowuje tablicę wierszy z symbolami,
zamienia symbole na cyfry systemu trójkowego
oraz znajduje największą liczbę w systemie dziesiętnym.
autor: 12345678901
**********************************************
*/
class SymbolConverter
{
private string[] tablica;
/*
**********************************************
nazwa metody: SymbolConverter (konstruktor)
opis metody: Inicjalizuje obiekt wartością tablicy z pliku.
parametry: dane - tablica napisów wczytanych z pliku symbole.txt
zwracany typ i opis: brak (konstruktor)
autor: 12345678901
**********************************************
*/
public SymbolConverter(string[] dane)
{
tablica = dane;
}
/*
**********************************************
nazwa metody: ConvertAndPrint
opis metody: Zamienia symbole +, o, * na cyfry 0, 1, 2 w każdym
wierszu tablicy i wypisuje wynik na ekran.
parametry: brak
zwracany typ i opis: brak (void)
autor: 12345678901
**********************************************
*/
public void ConvertAndPrint()
{
for (int i = 0; i < tablica.Length; i++)
{
tablica[i] = tablica[i]
.Replace('+', '0')
.Replace('o', '1')
.Replace('*', '2');
Console.WriteLine(tablica[i]);
}
}
/*
**********************************************
nazwa metody: GetLargestNumber
opis metody: Konwertuje każdy wiersz tablicy z systemu trójkowego
na dziesiętny i zwraca największą znalezioną liczbę.
parametry: brak
zwracany typ i opis: long - największa liczba w systemie dziesiętnym
autor: 12345678901
**********************************************
*/
public long GetLargestNumber()
{
long max = 0;
foreach (string row in tablica)
{
long value = Convert.ToInt64(row, 3);
if (value > max) max = value;
}
return max;
}
}
class Program
{
static void Main(string[] args)
{
string sciezka = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "symbole.txt");
string[] lines = File.ReadAllLines(sciezka);
SymbolConverter converter = new SymbolConverter(lines);
Console.WriteLine("Po konwersji symboli na cyfry:");
converter.ConvertAndPrint();
Console.WriteLine("\nNajwieksza liczba (dziesietnie): " + converter.GetLargestNumber());
Console.ReadKey();
}
}
}
Możesz używać komentarzy wieloliniowych /* ... */ albo kilku jednoliniowych // — oba są dozwolone. Najważniejsze, żeby każda metoda i konstruktor miała swój nagłówek.
Część 2. Aplikacja desktopowa (WPF)
Tworzenie projektu
- File → New → Project
- WPF Application (C#) — zwykłe, nie „.NET Framework”.
- Nazwa:
KalkulatorBiegacza.
Zobaczysz dwa kluczowe pliki:
MainWindow.xaml— wygląd okna (XML opisujący interfejs),MainWindow.xaml.cs— kod obsługujący zdarzenia (np. kliknięcia).
Błąd 4: Desktop ma tylko wygląd, bez obsługi przycisku
Spotykałem pracę, gdzie formatka wyglądała idealnie — kolory, czcionki, obraz biegacza, wszystko. Tylko że przycisk „Przelicz tempo” nic nie robił. To bardzo łatwo przeoczyć, bo aplikacja się uruchamia i wygląda dobrze.
Żeby przycisk działał, potrzebujesz dwóch rzeczy:
- W XAML:
Click="PrzeliczButton_Click"na przycisku. - W kodzie C#: metodę
PrzeliczButton_Click, która faktycznie coś liczy.
Sama wizualizacja przycisku to za mało.
Plik MainWindow.xaml
xml
<Window x:Class="KalkulatorBiegacza.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Kalkulator biegacza" Height="450" Width="600"
Background="Gray">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Obraz biegacza w lewym górnym rogu -->
<Image Source="biegacz.jpg" Grid.Row="0" Grid.RowSpan="3" Grid.Column="0"
Margin="10" Stretch="Uniform"/>
<!-- Nagłówek -->
<TextBlock Text="Kalkulator biegacza" Foreground="White" FontSize="40"
Grid.Row="0" Grid.Column="1" Margin="10"/>
<!-- Pole Kilometry -->
<StackPanel Orientation="Horizontal" Grid.Row="1" Grid.Column="1" Margin="10">
<TextBlock Text="Kilometry:" Foreground="White" FontSize="40" Width="200"/>
<TextBox x:Name="KilometryBox" Width="200" Height="50"
FontSize="30" Foreground="Gray" Background="White"/>
</StackPanel>
<!-- Pole Czas -->
<StackPanel Orientation="Horizontal" Grid.Row="2" Grid.Column="1" Margin="10">
<TextBlock Text="Czas:" Foreground="White" FontSize="40" Width="200"/>
<TextBox x:Name="CzasBox" Width="200" Height="50"
FontSize="30" Foreground="Gray" Background="White"
Text="GG:MM:SS"/>
</StackPanel>
<!-- Przycisk - UWAGA: Click="..." łączy go z metodą w kodzie -->
<Button x:Name="PrzeliczButton" Content="Przelicz tempo"
Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2"
Margin="10" Height="40" FontSize="20"
Background="Gray" Foreground="White"
Click="PrzeliczButton_Click"/>
<!-- Pole wyniku -->
<TextBlock x:Name="WynikText" Text="Twoje tempo wynosi: ..."
Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2"
Foreground="White" FontSize="40"
HorizontalAlignment="Center" Margin="10"/>
</Grid>
</Window>
Jak dodać obraz biegacza:
- Skopiuj plik
biegacz.jpgdo folderu projektu. - Solution Explorer → prawy klik → Add → Existing Item → wybierz plik.
- Properties → Build Action: Resource albo Copy to Output Directory: Copy always.
Błąd 5: Walidacja czasu sprawdzająca tylko długość i dwukropki
Spotykana walidacja wygląda mniej więcej tak:
csharp
// ŹLE - zaakceptuje np. "AB:CD:EF"
if (czas.Length == 8 && czas[2] == ':' && czas[5] == ':') { ... }
Wpiszesz AB:CD:EF — przejdzie. A potem int.Parse("AB") rzuca wyjątkiem i aplikacja się wywala.
Lepiej: użyj TimeSpan.TryParseExact. To wbudowana metoda C#, która sama sprawdza format i parsuje liczby:
csharp
bool ok = TimeSpan.TryParseExact(
czasNapis,
@"hh\:mm\:ss",
System.Globalization.CultureInfo.InvariantCulture,
out TimeSpan czas);
- Jeśli
okjestfalse→ format zły, pokazujesz komunikat. - Jeśli
true→ masz gotowy obiektTimeSpan czas, z którego łatwo wyciągniesz cokolwiek.
Jedna linia robi to, co inaczej zajęłoby Ci 15 linii ręcznych sprawdzeń. I robi to lepiej.
Błąd 6: Tempo liczone nieprawidłowo — nie z całkowitego czasu
Spotykałem podejście typu: „wyciągnę środkową liczbę z GG:MM:SS jako minuty i podzielę przez kilometry”. Dla 00:45:00na 10 km to przypadkiem wyjdzie dobrze — 45/10 = 4,5. Ale dla 01:30:00 na 10 km wyjdzie 30/10 = 3 min/km, a powinno być 9 min/km. Wpadka.
Poprawnie: zamień cały czas na sekundy, podziel przez kilometry, wynik przelicz z powrotem na minuty i sekundy.
Przykład z arkusza — 00:45:00 na 10 km:
00:45:00= 45 minut × 60 = 2700 sekund2700 / 10 = 270 sekund na kilometr270 sekund = 4 minuty 30 sekund→4:30 min/km✓
W kodzie:
csharp
double totalSeconds = czas.TotalSeconds; // 2700
double secondsPerKm = totalSeconds / kilometry; // 270
int minuty = (int)(secondsPerKm / 60); // 4
int sekundy = (int)(secondsPerKm % 60); // 30
string wynik = $"{minuty}:{sekundy:D2} min/km"; // "4:30 min/km"
{sekundy:D2} pilnuje, żeby sekundy zawsze były dwucyfrowe (czyli 4:05, nie 4:5).
Plik MainWindow.xaml.cs — kompletna logika
csharp
using System;
using System.Globalization;
using System.Windows;
namespace KalkulatorBiegacza
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
/*
**********************************************
nazwa metody: PrzeliczButton_Click
opis metody: Obsługuje kliknięcie przycisku "Przelicz tempo".
Waliduje dane wejściowe, oblicza tempo biegu
i wyświetla wynik lub komunikat o błędzie.
parametry: sender - obiekt wywołujący zdarzenie
e - argumenty zdarzenia kliknięcia
zwracany typ i opis: brak (void)
autor: 12345678901
**********************************************
*/
private void PrzeliczButton_Click(object sender, RoutedEventArgs e)
{
// 1) Walidacja kilometrów - musi być liczbą naturalną (>0)
if (!int.TryParse(KilometryBox.Text, out int kilometry) || kilometry <= 0)
{
WynikText.Text = "Niepoprawna liczba kilometrow";
return;
}
// 2) Walidacja czasu w formacie GG:MM:SS - prawidłowe parsowanie!
bool czasOk = TimeSpan.TryParseExact(
CzasBox.Text,
@"hh\:mm\:ss",
CultureInfo.InvariantCulture,
out TimeSpan czas);
if (!czasOk)
{
WynikText.Text = "Niepoprawny format czasu";
return;
}
// 3) Obliczenia - z CAŁKOWITEGO czasu w sekundach
double secondsPerKm = czas.TotalSeconds / kilometry;
int minuty = (int)(secondsPerKm / 60);
int sekundy = (int)(secondsPerKm % 60);
// 4) Wyświetlenie wyniku
WynikText.Text = $"Twoje tempo wynosi: {minuty}:{sekundy:D2} min/km";
}
}
}
Krótka wzmianka o .NET MAUI
Jeśli chcesz to samo zrobić w MAUI (np. żeby pobawić się aplikacją mobilną w domu), logika obliczeń jest identyczna. Różnice są tylko w XAML:
- Zamiast
<Window>używasz<ContentPage>. - Zamiast
<TextBlock>używasz<Label>. - Zamiast
Background="Gray"piszeszBackgroundColor="Gray". - Reszta (przyciski, TextBox-y, walidacja czasu, obliczenia) działa tak samo.
Na egzaminie INF.04 wystarczy WPF. MAUI traktuj jako dodatek do nauki na własne potrzeby.
Część 3. Dokumentacja — Błąd 7
Dokumentacja w komentarzach kodu, nie tylko w pliku egzamin.docx
Plik egzamin.docx z opisem narzędzi to nie wszystko. Arkusz wymaga trzech rzeczy:
1. Komentarze nagłówkowe nad każdą metodą aplikacji konsolowej — według szablonu z Listingu 1. Masz to pokazane w kompletnym kodzie konsolowym wyżej. Pamiętaj o konstruktorze — on też potrzebuje komentarza.
2. Plik egzamin.docx w folderze dokumentacja z informacjami o narzędziach:
System operacyjny: Windows 11
Środowisko programistyczne: Visual Studio 2026
Język programowania: C#
Framework aplikacji desktopowej: WPF (.NET 8)
3. Zrzuty ekranu też w folderze dokumentacja:
konsola1.png,konsola2.png— działająca aplikacja konsolowa + widoczne IDE,des1.png— stan początkowy (puste pola),des2.png— poprawne obliczenie tempa,des3.png— komunikat o błędnym formacie czasu.
Klawisz PrtScn robi zrzut całego ekranu. Wklej go w Paint i zapisz jako .png. Pasek zadań musi być widoczny — to wymóg arkusza.
Checklist przed oddaniem płyty
Przejdź przez tę listę zanim zapakujesz płytę:
Aplikacja konsolowa:
- Jest osobna klasa, nie wszystko w
Main. - Pole tablicowe ma modyfikator
private. - Jest konstruktor przyjmujący tablicę.
- Metoda zamieniająca symbole i wypisująca tablicę działa.
- Metoda zwracająca największą liczbę faktycznie liczy, a nie zwraca
0. -
symbole.txtwczytywany ze ścieżki względnej, nieC:\.... - Każda metoda i konstruktor ma komentarz nagłówkowy.
Aplikacja desktopowa:
- Przycisk ma
Click="..."w XAML. - Metoda
PrzeliczButton_Clickistnieje w.csi coś robi. - Walidacja czasu używa
TimeSpan.TryParseExact, nie tylko.Length. - Tempo liczone z całkowitych sekund:
czas.TotalSeconds / km. - Komunikat błędu pojawia się przy niepoprawnym czasie.
- Obraz biegacza jest widoczny.
- Kolory się zgadzają: szare tło, biała czcionka 40px, inputy białe z szarą czcionką 30px.
Struktura plików na płycie:
[numer_zdającego]/
├── konsolowa/
│ ├── konsola.zip
│ ├── Program.cs
│ └── KonsolaSymbole.exe (jeśli istnieje)
├── desktopowa/
│ ├── des.zip
│ ├── MainWindow.xaml
│ └── MainWindow.xaml.cs
└── dokumentacja/
├── egzamin.docx
├── konsola1.png
├── konsola2.png
├── des1.png
├── des2.png
└── des3.png

