Konwersje typów

Użytkownik wpisuje z klawiatury tekst, a ty potrzebujesz liczby. Obliczenie daje ułamek, a ty chcesz liczbę całkowitą. Ta lekcja pokazuje, jak zamieniać jeden typ na drugi — i jak nie stracić przy tym danych ani nie wywrócić programu.

C# rzutowanie Parse TryParse 50 min
CEL LEKCJI

Czego się dziś nauczysz

  • Wyjaśnisz, kiedy konwersja dzieje się sama, a kiedy trzeba o nią poprosić
  • Zapiszesz rzutowanie i wskażesz, jakie dane przy nim tracisz
  • Zamienisz tekst na liczbę metodą Parse i TryParse
  • Wyjaśnisz, dlaczego TryParse jest bezpieczniejsze
  • Zamienisz liczbę na tekst i nadasz jej format
  • Wskażesz różnicę między Parse, TryParse a klasą Convert
TEORIA

Po co w ogóle konwersje

C# pilnuje typów bardzo rygorystycznie. Nie pozwoli włożyć tekstu do zmiennej liczbowej ani wykonać działania na niepasujących typach.

Program.cs
string wiekTekst = "17";

// int rok = 2026 - wiekTekst;
//                  └── CS0019: nie można odjąć tekstu od liczby

Program musi więc najpierw przekształcić napis "17" w liczbę 17. Taka zamiana jednego typu w drugi to konwersja.

To nie jest to samo, co „zmiana typu zmiennej”

Typu istniejącej zmiennej nie da się zmienić — on jest ustalony raz na zawsze. Konwersja tworzy nową wartość innego typu na podstawie starej. Oryginał zostaje nietknięty.

W C# konwersje dzielą się na dwie grupy: te, które kompilator wykona sam, i te, o które musisz poprosić.

TEORIA

Konwersja niejawna — dzieje się sama

Gdy nowy typ jest „szerszy” od starego i na pewno pomieści każdą wartość, kompilator wykonuje konwersję bez pytania. Nic nie może się zgubić, więc nie ma o czym dyskutować.

Program.cs
int liczba = 42;

long dluga = liczba;        // int  → long    zawsze bezpieczne
double ulamek = liczba;     // int  → double  zawsze bezpieczne
decimal kwota = liczba;     // int  → decimal zawsze bezpieczne

byte maly = 200;
int wiekszy = maly;         // byte → int     zawsze bezpieczne

Console.WriteLine($"{dluga} {ulamek} {kwota} {wiekszy}");   // 42 42 42 200

Kierunki, w których konwersja jest automatyczna

kierunek poszerzania
byte  →  short  →  int  →  long  →  float  →  double
                    ↓        ↓
                    └────────┴──→  decimal

char  →  int  (i dalej jak wyżej)
ZasadaWyjaśnienie
Węższy w szerszy — takKażda liczba typu int zmieści się w long. Nie ma ryzyka.
Całkowity w zmiennoprzecinkowy — takint wchodzi do double i decimal automatycznie.
Zmiennoprzecinkowy w decimalniedouble ma szerszy zakres niż decimal, więc automatu nie ma. Trzeba rzutować.
decimal w doublenieStraciłbyś dokładność, na której właśnie zależało. Trzeba rzutować świadomie.
Cokolwiek na stringnieTekst to zupełnie inna kategoria. Służą do tego metody, nie rzutowanie.

Bezpieczne nie znaczy dokładne

int → float jest automatyczne, bo każda liczba int mieści się w zakresie float. Ale float ma tylko około 7 cyfr znaczących, więc bardzo duże liczby całkowite tracą precyzję:

int duza = 16777217; po zamianie na float i z powrotem daje 16777216.

Dlatego do liczb całkowitych, które mają zostać dokładne, nie używa się float.

TEORIA

Rzutowanie — gdy coś może się zgubić

Gdy nowy typ jest węższy, kompilator odmawia współpracy. Musisz sam potwierdzić, że wiesz, co robisz — służy do tego rzutowanie: nazwa typu w nawiasie przed wartością.

Program.cs
double cena = 123.87;

// int calkowita = cena;              // CS0266 — kompilator odmawia
int calkowita = (int)cena;            // ✓ prosimy wprost

Console.WriteLine(calkowita);         // 123 — część ułamkowa UCIĘTA, nie zaokrąglona

Rzutowanie ucina, nie zaokrągla

(int)123.87 daje 123, a nie 124. Rzutowanie po prostu odrzuca wszystko po przecinku, niezależnie od tego, jak blisko było do następnej liczby. (int)0.99 daje zero.

Jeśli chcesz zaokrąglić, użyj Math.Round — a dopiero potem rzutuj: (int)Math.Round(123.87) daje 124.

Zawężanie liczb całkowitych

Program.cs
int mala = 200;
byte b1 = (byte)mala;         // 200 — mieści się, wszystko dobrze
Console.WriteLine(b1);        // 200

int duza = 300;
byte b2 = (byte)duza;         // 300 NIE mieści się w bajcie!
Console.WriteLine(b2);        // 44  — i nikt nie ostrzega

Skąd 44? Bajt mieści liczby 0–255, więc 300 „przekręca się” o pełny zakres: 300 − 256 = 44. To ten sam mechanizm, który poznałeś w lekcji 06.

Rzutowanie to obietnica, którą składasz kompilatorowi

Pisząc (byte), mówisz: „sprawdziłem, ta wartość się zmieści”. Kompilator ci wierzy i przestaje pilnować. Jeśli obietnica była fałszywa, dostaniesz cichy, błędny wynik — bez żadnego komunikatu.

Dlatego przed rzutowaniem warto sprawdzić zakres albo użyć słowa checked, które zamienia ciche przekręcenie w wyjątek.

TEORIA

Liczba na tekst — zawsze się udaje

To najprostszy kierunek. Każda wartość w C# potrafi opisać się tekstem metodą ToString().

Program.cs
int liczba = 42;
double ulamek = 3.14159;
bool flaga = true;

string t1 = liczba.ToString();       // "42"
string t2 = ulamek.ToString();       // "3,14159"
string t3 = flaga.ToString();        // "True"

// Z formatem
string t4 = ulamek.ToString("F2");   // "3,14"

// Najczęściej i tak używamy interpolacji — ona wywołuje ToString za nas
Console.WriteLine($"{liczba} {ulamek:F2} {flaga}");   // 42 3,14 True

Wartość logiczna zamienia się na "True" lub "False"

Z wielkiej litery i po angielsku — tak zdefiniowano to w bibliotece .NET. Jeśli chcesz pokazać użytkownikowi „tak” i „nie”, musisz to zrobić sam, instrukcją warunkową. Poznasz ją w następnym bloku.

TEORIA

Tekst na liczbę — tu bywa trudno

Ten kierunek jest ryzykowny, bo nie każdy tekst da się zamienić na liczbę. Co ma zrobić program, gdy dostanie "abc" albo pusty napis? C# daje dwie odpowiedzi na to pytanie.

Sposób 1: Parse — przerywa program przy błędzie

Program.cs
string tekst = "123";
int liczba = int.Parse(tekst);
Console.WriteLine(liczba + 1);        // 124

// A gdy tekst nie jest liczbą:
string zly = "sto dwadziescia trzy";
// int x = int.Parse(zly);            // FormatException — program się wywraca

Sposób 2: TryParse — mówi, czy się udało

Program.cs
string tekst = "123";

bool sukces = int.TryParse(tekst, out int wynik);

Console.WriteLine(sukces);      // True
Console.WriteLine(wynik);       // 123

// Przy błędnym tekście program działa dalej
bool sukces2 = int.TryParse("abc", out int wynik2);

Console.WriteLine(sukces2);     // False
Console.WriteLine(wynik2);      // 0 — wartość domyślna, bo konwersja się nie udała

Co robi słowo out

TryParse musi zwrócić dwie informacje naraz: czy się udało oraz jaki jest wynik. Metoda potrafi zwrócić tylko jedną wartość, więc druga jest przekazywana przez parametr oznaczony słowem out.

Zapis out int wynik jednocześnie deklaruje zmienną i przekazuje ją do metody, żeby ta mogła ją wypełnić. Po wywołaniu zmienna wynik jest dostępna w twoim kodzie. Do parametrów out wrócimy dokładniej w bloku o metodach.

Które wybrać

MetodaGdy tekst jest błędnyKiedy używać
int.Parse(t) przerywa program wyjątkiem Gdy masz pewność, że tekst jest poprawny — np. pochodzi z twojego własnego kodu, nie od użytkownika.
int.TryParse(t, out x) zwraca false, program działa dalej Zawsze przy danych od użytkownika, z pliku albo z sieci — czyli wszędzie tam, gdzie błąd jest normalną możliwością.

Zasada na cały kurs

Cokolwiek wpisuje użytkownik — TryParse. Nie dlatego, że Parse jest złe, tylko dlatego, że użytkownik prędzej czy później wpisze coś nieoczekiwanego, a program nie powinien się od tego wywracać.

Pozostałe typy działają tak samo

Program.cs
double.TryParse("3,14", out double d);     // przecinek — polskie ustawienia
decimal.TryParse("19,99", out decimal m);
bool.TryParse("true", out bool b);         // działa też "True", "TRUE"
long.TryParse("9000000000", out long l);

Przecinek czy kropka?

double.TryParse patrzy na ustawienia regionalne systemu. Na polskim Windowsie separatorem części ułamkowej jest przecinek, więc "3,14" zadziała, a "3.14" zostanie odrzucone albo — co gorsza — zrozumiane jako 314.

To bywa źródłem trudnych do znalezienia błędów, zwłaszcza przy czytaniu plików utworzonych na innym komputerze. Jak to opanować, pokazujemy w następnej lekcji, przy okazji formatowania.

METODY WBUDOWANE

Klasa Convert

Trzecia droga: klasa Convert z zestawem metod zamieniających dowolny typ na dowolny inny.

MetodaZwracaUwagi
Convert.ToInt32(x)intPrzyjmuje tekst, liczbę, wartość logiczną. Dla null zwraca 0 zamiast błędu.
Convert.ToDouble(x)double
Convert.ToDecimal(x)decimal
Convert.ToBoolean(x)boolDla liczb: 0 to false, każda inna to true.
Convert.ToString(x)stringZ drugim argumentem zamienia liczbę na inny system: Convert.ToString(20, 2) daje "10100".
Program.cs
// Convert zaokrągla, a rzutowanie ucina — to ważna różnica!
double x = 4.7;

Console.WriteLine((int)x);                 // 4  — ucięcie
Console.WriteLine(Convert.ToInt32(x));     // 5  — zaokrąglenie

// Convert radzi sobie z brakiem wartości
string brak = null;
Console.WriteLine(Convert.ToInt32(brak));  // 0  — zamiast błędu
SposóbPrzy błędnym tekściePrzy liczbie z ułamkiem
(int) — rzutowanienie dotyczyucina część ułamkową
int.Parsewyjąteknie dotyczy
int.TryParsezwraca falsenie dotyczy
Convert.ToInt32wyjątekzaokrągla

Czego używać w praktyce

Do danych od użytkownika — zawsze TryParse.
Do zamiany liczby na liczbę — rzutowanie, ewentualnie Math.Round plus rzutowanie, gdy chcesz zaokrąglić.
Convert przydaje się głównie tam, gdzie wartość może być pusta, oraz do zamiany systemów liczbowych.

TEORIA

Znak a liczba

Typ char to w gruncie rzeczy liczba — numer znaku w tablicy Unicode. Dlatego konwersje między char a int są bardzo proste.

Program.cs
char litera = 'A';

int kod = litera;                 // char → int, automatycznie: 65
char zKodu = (char)66;            // int → char, rzutowanie: 'B'

Console.WriteLine($"{kod} {zKodu}");     // 65 B

// Następna litera alfabetu
char nastepna = (char)(litera + 1);      // 'B'
Console.WriteLine(nastepna);

Znak cyfry to nie jest ta cyfra

Znak '7' ma w tablicy Unicode numer 55, a nie 7. To bardzo częste źródło pomyłek.

Program.cs
char cyfra = '7';

Console.WriteLine((int)cyfra);          // 55  ✗ numer znaku, nie wartość

// Sposób 1: odejmujemy numer znaku zera
int wartosc1 = cyfra - '0';             // 55 - 48 = 7   ✓

// Sposób 2: gotowa metoda
int wartosc2 = (int)char.GetNumericValue(cyfra);   // 7  ✓

Console.WriteLine($"{wartosc1} {wartosc2}");       // 7 7

Sztuczka z odejmowaniem '0' działa, bo cyfry mają w tablicy Unicode kolejne numery: '0' to 48, '1' to 49, i tak dalej aż do '9' = 57. Odjęcie numeru zera daje więc wartość cyfry.

PRZYKŁAD Z OMÓWIENIEM

Paragon — wszystkie konwersje naraz

Program dostaje dane w postaci tekstu (tak, jakby przyszły z formularza), zamienia je na liczby, liczy i wypisuje wynik z powrotem jako tekst.

Program.cs
// Dane „z formularza" — zawsze przychodzą jako tekst
string sztukTekst = "3";
string cenaTekst = "24,99";
string rabatTekst = "abc";        // celowo błędny

const decimal StawkaVat = 0.23m;

// Konwersje z zabezpieczeniem
bool okSztuk = int.TryParse(sztukTekst, out int sztuk);
bool okCena = decimal.TryParse(cenaTekst, out decimal cenaJednostkowa);
bool okRabat = decimal.TryParse(rabatTekst, out decimal rabatProcent);

Console.WriteLine($"Sztuki wczytane: {okSztuk}  -> {sztuk}");
Console.WriteLine($"Cena wczytana:   {okCena}  -> {cenaJednostkowa}");
Console.WriteLine($"Rabat wczytany:  {okRabat} -> {rabatProcent}");
Console.WriteLine();

// Obliczenia — sztuk to int, cena to decimal; int wchodzi w decimal automatycznie
decimal netto = sztuk * cenaJednostkowa;
decimal vat = netto * StawkaVat;
decimal brutto = netto + vat;

// Ile pełnych sztuk zmieści się w budżecie 50 zl
decimal budzet = 50m;
int zaBudzet = (int)(budzet / cenaJednostkowa);      // rzutowanie UCINA — i o to chodzi

Console.WriteLine($"Netto:  {netto,8:F2} zl");
Console.WriteLine($"VAT:    {vat,8:F2} zl");
Console.WriteLine($"Brutto: {brutto,8:F2} zl");
Console.WriteLine();
Console.WriteLine($"Za {budzet:F2} zl kupisz {zaBudzet} szt.");
wynik w konsoli
Sztuki wczytane: True  -> 3
Cena wczytana:   True  -> 24,99
Rabat wczytany:  False -> 0

Netto:     74,97 zl
VAT:       17,24 zl
Brutto:    92,21 zl

Za 50,00 zl kupisz 2 szt.

Omówienie

FragmentDlaczego tak
Wszystkie dane jako string Tak wygląda rzeczywistość: z klawiatury, z pliku i z formularza dane przychodzą jako tekst. Konwersja jest zawsze pierwszym krokiem.
TryParse zamiast Parse Celowo podaliśmy błędny rabat. Program nie wywrócił się — po prostu zapisał, że konwersja się nie udała, a zmienna dostała zero.
sztuk * cenaJednostkowa int pomnożony przez decimal daje decimal — kompilator sam poszerza węższy typ. Rzutowanie nie jest potrzebne.
(int)(budzet / cenaJednostkowa) 50 / 24,99 to 2,0008. Rzutowanie ucina do 2 — i tego właśnie chcemy, bo nie kupimy dwóch sztuk i ośmiu tysięcznych. To przykład, w którym ucinanie jest zaletą, a nie pułapką.
50m Przyrostek m jest konieczny — bez niego byłby to double, którego nie da się podzielić przez decimal bez rzutowania.
{netto,8:F2} Dwa formaty naraz: 8 to szerokość kolumny (wyrównanie do prawej), F2 to dwa miejsca po przecinku. Dzięki temu kwoty ładnie się układają jedna pod drugą.
CZĘSTE BŁĘDY

Na co uważać

KodProblem
int x = 3.99; CS0266 — konwersja zawężająca wymaga rzutowania: (int)3.99.
int x = (int)3.99; licząc na 4 Da 3. Rzutowanie ucina. Do zaokrąglenia użyj (int)Math.Round(3.99).
int.Parse(Console.ReadLine()) Kompiluje się, ale przerwie program, gdy użytkownik wpisze cokolwiek innego niż liczbę. Użyj TryParse.
double.Parse("3.14") na polskim systemie Kropka nie jest tu separatorem ułamka. Albo dane muszą używać przecinka, albo trzeba podać kulturę — o tym w następnej lekcji.
int w = cyfra; gdy cyfra = '7' Da 55, nie 7. Trzeba cyfra - '0'.
string s = (string)liczba; CS0030 — na tekst się nie rzutuje. Użyj liczba.ToString() albo interpolacji.
byte b = (byte)300; Kompiluje się, ale da 44. Rzutowanie nie ostrzega przed przekroczeniem zakresu.
decimal d = 19.99; CS0664 — literał bez przyrostka jest typu double, a double → decimal nie jest automatyczne. Potrzebne 19.99m.
if (int.TryParse(t, out int x)) { }
a potem użycie x na zewnątrz
Zmienna zadeklarowana w wywołaniu TryParse jest widoczna od tego miejsca do końca bloku — poza blokiem już nie. To ten sam zasięg, który poznałeś w lekcji 04.
ZADANIA

Zadania

ZAD 1Przewidz wynik★☆☆

Zapisz wynik każdej linii, zanim uruchomisz program.

co wypisze?
Console.WriteLine((int)9.99);
Console.WriteLine((int)(-9.99));
Console.WriteLine(Convert.ToInt32(9.99));
Console.WriteLine((int)Math.Round(9.5));
Console.WriteLine((char)(65 + 5));
Console.WriteLine('9' - '0');
Console.WriteLine((byte)260);
pokaż rozwiązanie
9
-9
10
10
F
9
4

Linie 1 i 2 — rzutowanie ucina w stronę zera, więc z −9,99 robi się −9, a nie −10.

Linia 3Convert zaokrągla, więc 9,99 daje 10.

Linia 4Math.Round(9.5) daje 10.

Linia 5 — 65 to 'A', więc 70 to 'F'.

Linia 7 — 260 nie mieści się w bajcie: 260 − 256 = 4.

ZAD 2Bezpieczne wczytanie★☆☆

Masz trzy napisy: "42", "3,5" i "pies". Spróbuj zamienić każdy z nich na int metodą TryParse i wypisz, czy się udało oraz jaki wyszedł wynik.

pokaż rozwiązanie
string t1 = "42";
string t2 = "3,5";
string t3 = "pies";

bool ok1 = int.TryParse(t1, out int w1);
bool ok2 = int.TryParse(t2, out int w2);
bool ok3 = int.TryParse(t3, out int w3);

Console.WriteLine($"\"{t1}\"  -> udalo sie: {ok1,-5}  wynik: {w1}");
Console.WriteLine($"\"{t2}\" -> udalo sie: {ok2,-5}  wynik: {w2}");
Console.WriteLine($"\"{t3}\" -> udalo sie: {ok3,-5}  wynik: {w3}");
"42"  -> udalo sie: True   wynik: 42
"3,5" -> udalo sie: False  wynik: 0
"pies" -> udalo sie: False  wynik: 0

Ciekawy jest drugi przypadek. "3,5" to poprawna liczba, ale nie całkowita — a prosiliśmy o int. Konwersja się nie udaje. Ten sam napis przekazany do double.TryParse zadziałałby bez problemu.

\" w tekście to sposób na wstawienie cudzysłowu do napisu. Bez ukośnika kompilator uznałby, że w tym miejscu napis się kończy.

ZAD 3Ucinanie kontra zaokrąglanie★★☆

Dla liczb 2,3, 2,5 i 2,7 wypisz obok siebie wynik rzutowania (int), Math.Round, Math.Floor i Math.Ceiling. Ułóż to w czytelną tabelkę.

pokaż rozwiązanie
double a = 2.3, b = 2.5, c = 2.7;

Console.WriteLine("liczba   (int)   Round   Floor   Ceiling");
Console.WriteLine("----------------------------------------");
Console.WriteLine($"{a,6}{(int)a,8}{Math.Round(a),8}{Math.Floor(a),8}{Math.Ceiling(a),10}");
Console.WriteLine($"{b,6}{(int)b,8}{Math.Round(b),8}{Math.Floor(b),8}{Math.Ceiling(b),10}");
Console.WriteLine($"{c,6}{(int)c,8}{Math.Round(c),8}{Math.Floor(c),8}{Math.Ceiling(c),10}");
liczba   (int)   Round   Floor   Ceiling
----------------------------------------
   2,3       2       2       2         3
   2,5       2       2       2         3
   2,7       2       3       2         3

Najciekawszy jest wiersz środkowy. Math.Round(2.5) daje 2, a nie 3! To nie błąd — .NET domyślnie stosuje zaokrąglanie bankierskie: przy dokładnej połówce wynik idzie do najbliższej liczby parzystej. Dzięki temu przy wielu zaokrągleniach błędy nie kumulują się w jedną stronę.

Gdy potrzebujesz szkolnego zaokrąglania „połowa zawsze w górę”, podaj to wprost: Math.Round(2.5, MidpointRounding.AwayFromZero) da 3.

To jedna z tych rzeczy, które potrafią zaskoczyć na egzaminie — warto o niej pamiętać.

ZAD 4Kalkulator kieszonkowego★★☆

Dane przychodzą jako tekst: kwota tygodniowa "37,50" i liczba tygodni "8". Oblicz łączną kwotę oraz ile pełnych gier po 149,99 zł da się za nią kupić. Użyj TryParse i odpowiednich typów.

pokaż rozwiązanie
string kwotaTekst = "37,50";
string tygodnieTekst = "8";
const decimal CenaGry = 149.99m;

decimal.TryParse(kwotaTekst, out decimal naTydzien);
int.TryParse(tygodnieTekst, out int tygodnie);

decimal lacznie = naTydzien * tygodnie;
int ileGier = (int)(lacznie / CenaGry);
decimal zostanie = lacznie - ileGier * CenaGry;

Console.WriteLine($"Na tydzien:  {naTydzien:F2} zl");
Console.WriteLine($"Tygodni:     {tygodnie}");
Console.WriteLine($"Lacznie:     {lacznie:F2} zl");
Console.WriteLine($"Gier:        {ileGier}");
Console.WriteLine($"Zostanie:    {zostanie:F2} zl");
Na tydzien:  37,50 zl
Tygodni:     8
Lacznie:     300,00 zl
Gier:        2
Zostanie:    0,02 zl

Kwoty trzymamy w decimal, bo to pieniądze. Liczbę tygodni w int, bo to sztuki.

lacznie / CenaGry daje 2,0001…, a rzutowanie ucina do 2 — dokładnie tego chcemy, bo za resztę nie kupimy trzeciej gry.

Zwróć uwagę, że ileGier * CenaGry miesza int z decimal — i działa bez rzutowania, bo int poszerza się automatycznie.

ZAD 5Suma cyfr z tekstu★★★

Mając napis "48317", oblicz sumę jego cyfr. Nie używaj pętli — sięgaj po kolejne znaki przez indeks i zamieniaj je na wartości liczbowe.

pokaż rozwiązanie
string tekst = "48317";

int c1 = tekst[0] - '0';
int c2 = tekst[1] - '0';
int c3 = tekst[2] - '0';
int c4 = tekst[3] - '0';
int c5 = tekst[4] - '0';

int suma = c1 + c2 + c3 + c4 + c5;

Console.WriteLine($"Cyfry: {c1} {c2} {c3} {c4} {c5}");
Console.WriteLine($"Suma:  {suma}");
Cyfry: 4 8 3 1 7
Suma:  23

tekst[0] zwraca pojedynczy znak typu char, a nie napis. Odejmowanie '0' zamienia go na wartość liczbową.

Gdybyś napisał int.Parse(tekst[0]), kompilator zaprotestowałby — Parse przyjmuje napis, nie znak. Zadziałałoby dopiero int.Parse(tekst[0].ToString()), ale wersja z odejmowaniem jest krótsza i szybsza.

Po lekcji o pętlach wrócisz do tego zadania i napiszesz wersję działającą dla napisu o dowolnej długości.

ZAD 6Znajdź cztery błędy★★★

Program ma policzyć średnią cenę trzech produktów i pokazać ją w groszach. Nie działa poprawnie — znajdź i popraw cztery usterki.

do poprawienia
string c1 = "12,50", c2 = "8,99", c3 = "abc";

decimal cena1 = decimal.Parse(c1);
decimal cena2 = decimal.Parse(c2);
decimal cena3 = decimal.Parse(c3);

decimal srednia = cena1 + cena2 + cena3 / 3;
int grosze = srednia * 100;

Console.WriteLine("Srednia w groszach: " + grosze);
pokaż rozwiązanie
string c1 = "12,50", c2 = "8,99", c3 = "abc";

// 1. TryParse zamiast Parse — trzeci napis nie jest liczbą
decimal.TryParse(c1, out decimal cena1);
decimal.TryParse(c2, out decimal cena2);
decimal.TryParse(c3, out decimal cena3);      // false, cena3 = 0

// 2. nawias — dzielenie miało dotyczyć całej sumy
decimal srednia = (cena1 + cena2 + cena3) / 3;

// 3. rzutowanie — decimal nie wejdzie do int bez pytania
int grosze = (int)(srednia * 100);

// 4. interpolacja zamiast sklejania plusem
Console.WriteLine($"Srednia w groszach: {grosze}");
Srednia w groszach: 716

Błąd 1decimal.Parse("abc") przerywa program wyjątkiem FormatException. Przy danych, które mogą być błędne, zawsze TryParse.

Błąd 2 — brak nawiasu. Dzielenie wykonałoby się tylko na cena3, a nie na sumie. Kod się kompiluje i wypisuje bzdurę.

Błąd 3CS0266, jedyny błąd zatrzymujący kompilację. decimal nie zamieni się w int automatycznie.

Błąd 4 — sklejanie plusem działa, ale interpolacja jest czytelniejsza i odporniejsza na pomyłki.

Wynik: (12,50 + 8,99 + 0) / 3 = 7,1633…, po pomnożeniu przez 100 i ucięciu — 716 groszy.

PODSUMOWANIE

Co trzeba zapamiętać

  • Konwersja niejawna dzieje się sama, gdy nic nie może się zgubić: int → long → double.
  • Rzutowanie (typ)wartosc to twoja obietnica, że wiesz, co robisz. Kompilator przestaje pilnować.
  • Rzutowanie (int) ucina część ułamkową. Convert.ToInt32 zaokrągla.
  • Zawężanie liczb całkowitych poza zakres daje cichy, błędny wynik — bez ostrzeżenia.
  • Na tekst zamieniamy metodą ToString() albo interpolacją. Rzutowanie na string nie istnieje.
  • Parse przerywa program przy błędnym tekście, TryParse zwraca false i działa dalej.
  • Dane od użytkownika zawsze przez TryParse.
  • Słowo out pozwala metodzie zwrócić drugą wartość obok tej głównej.
  • Znak cyfry to nie jej wartość: '7' ma numer 55. Wartość liczbową daje cyfra - '0'.
  • Separator ułamka zależy od ustawień regionalnych — na polskim systemie to przecinek.