Wprowadzenie do konwersji typów
Konwersja typów to proces przekształcania wartości z jednego typu danych w inny. Jest to fundamentalna operacja w programowaniu, która pozwala nam:
- Łączyć dane różnych typów w obliczeniach
- Przetwarzać dane wejściowe od użytkownika (zwykle string → inne typy)
- Dostosowywać dane do wymagań różnych metod i operacji
- Bezpiecznie przekazywać dane między komponentami aplikacji
Dlaczego potrzebujemy konwersji?
// Bez konwersji - błąd kompilacji!
string wiek = "25";
int rokUrodzenia = 2024 - wiek; // ❌ Błąd! Nie można odejmować string od int
// Z konwersją - działa poprawnie
string wiekTekst = "25";
int wiekLiczba = int.Parse(wiekTekst); // Konwersja string → int
int rokUrodzenia = 2024 - wiekLiczba; // ✅ OK!
Console.WriteLine($"Rok urodzenia: {rokUrodzenia}");Rodzaje konwersji typów
1. Konwersja niejawna (Implicit Conversion)
Odbywa się automatycznie przez kompilator, gdy:
- Nie ma ryzyka utraty danych
- Typ docelowy może pomieścić wszystkie wartości typu źródłowego
- Konwersja jest zawsze bezpieczna
Hierarchia konwersji niejawnych dla typów liczbowych:
byte → short → int → long → float → double
↘ ↘ ↘ ↘ ↘
char → ushort → uint → ulong
decimal (osobna ścieżka od byte, short, int, long, char, ushort, uint)Przykłady konwersji niejawnych:
// Liczby całkowite - poszerzanie zakresu
byte malaByte = 100;
int większyInt = malaByte; // byte → int (automatycznie)
long dużyLong = większyInt; // int → long (automatycznie)
// Liczby zmiennoprzecinkowe
float liczbaFloat = 3.14f;
double liczbaDouble = liczbaFloat; // float → double (automatycznie)
// Mieszane konwersje
int liczbaInt = 42;
float liczbaFloat2 = liczbaInt; // int → float (automatycznie)
double liczbaDouble2 = liczbaInt; // int → double (automatycznie)
// Wyświetlenie wyników
Console.WriteLine($"byte {malaByte} → int {większyInt}");
Console.WriteLine($"int {liczbaInt} → double {liczbaDouble2}");Uwagi o konwersjach niejawnych:
// Uwaga: int → float może spowodować utratę precyzji (ale nie zakresu)
int dużaLiczba = 16777217; // Więcej niż 24 bity precyzji float
float jakofloat = dużaLiczba;
int z_powrotem = (int)jakofloat;
Console.WriteLine($"Oryginalna: {dużaLiczba}");
Console.WriteLine($"Jako float: {jakofloat}");
Console.WriteLine($"Z powrotem: {z_powrotem}");
// Może pokazać różne wartości przez ograniczoną precyzję float!2. Konwersja jawna (Explicit Conversion) – Casting
Wymaga ręcznego rzutowania, gdy:
- Istnieje ryzyko utraty danych
- Kompilator nie może zagwarantować bezpieczeństwa
- Konwertujemy z większego typu na mniejszy
Składnia casting:
typDocelowy zmienna = (typDocelowy)źródło;Przykłady konwersji jawnych:
// Zawężanie zakresu liczbowego
double liczbaDouble = 123.456;
int liczbaInt = (int)liczbaDouble; // Utrata części ułamkowej!
float liczbaFloat = (float)liczbaDouble; // Możliwa utrata precyzji
Console.WriteLine($"double: {liczbaDouble}");
Console.WriteLine($"int: {liczbaInt}"); // 123 (bez części ułamkowej)
Console.WriteLine($"float: {liczbaFloat}");
// Zawężanie między typami całkowitymi
long dużaLong = 3000000000L; // Przekracza zakres int
int mniejszyInt = (int)dużaLong; // Może spowodować overflow!
Console.WriteLine($"long: {dużaLong}");
Console.WriteLine($"int: {mniejszyInt}"); // Może być negatywny przez overflow
// Bezpieczne sprawdzanie granic przed casting
long wartość = 2500000000L;
if (wartość >= int.MinValue && wartość <= int.MaxValue)
{
int bezpiecznyInt = (int)wartość;
Console.WriteLine($"Bezpieczna konwersja: {bezpiecznyInt}");
}
else
{
Console.WriteLine($"Wartość {wartość} przekracza zakres int!");
}Konwersje między typami liczbowymi – szczegóły
Tabela konwersji z potencjalnymi problemami:
| Od → Do | Niejawna | Jawna | Potencjalne problemy |
|---|---|---|---|
byte → int | ✅ | – | Brak |
int → byte | ❌ | ✅ | Overflow jeśli > 255 |
int → float | ✅ | – | Utrata precyzji dla dużych liczb |
float → int | ❌ | ✅ | Utrata części ułamkowej |
double → decimal | ❌ | ✅ | Możliwy overflow |
decimal → double | ❌ | ✅ | Utrata precyzji |
Praktyczne przykłady z różnymi typami:
// Praca z różnymi typami liczbowymi
Console.WriteLine("=== KONWERSJE MIĘDZY TYPAMI LICZBOWYMI ===");
// byte, short, int, long
byte małaWartość = 50;
short średniaWartość = małaWartość; // Niejawne: byte → short
int dużaWartość = średniaWartość; // Niejawne: short → int
long największaWartość = dużaWartość; // Niejawne: int → long
Console.WriteLine($"byte(50) → short({średniaWartość}) → int({dużaWartość}) → long({największaWartość})");
// Zawężanie - wymaga jawnego casting
byte z_powrotem_byte = (byte)średniaWartość; // short → byte
Console.WriteLine($"Z powrotem do byte: {z_powrotem_byte}");
// Typy zmiennoprzecinkowe
float precyzjaJedna = 3.14159f;
double precyzjaDwa = precyzjaJedna; // Niejawne: float → double
decimal precyzjaFinansowa = (decimal)precyzjaDwa; // Jawne: double → decimal
Console.WriteLine($"float: {precyzjaJedna}");
Console.WriteLine($"double: {precyzjaDwa}");
Console.WriteLine($"decimal: {precyzjaFinansowa}");
// Problemy z precyzją
float dużaLiczbaFloat = 16777216f + 1f; // float ma ~7 cyfr precyzji
Console.WriteLine($"16777216 + 1 jako float: {dużaLiczbaFloat}"); // Może wyświetlić 16777216!Konwersje string ↔ inne typy
Konwersja typów → string (zawsze bezpieczna)
Każdy typ ma metodę .ToString():
// Podstawowe konwersje na string
int liczbaInt = 42;
double liczbaDouble = 3.14159;
bool wartośćLogiczna = true;
char znak = 'A';
DateTime data = DateTime.Now;
Console.WriteLine("=== KONWERSJE NA STRING ===");
Console.WriteLine($"int → string: '{liczbaInt.ToString()}'");
Console.WriteLine($"double → string: '{liczbaDouble.ToString()}'");
Console.WriteLine($"bool → string: '{wartośćLogiczna.ToString()}'");
Console.WriteLine($"char → string: '{znak.ToString()}'");
Console.WriteLine($"DateTime → string: '{data.ToString()}'");
// Formatowanie podczas konwersji
Console.WriteLine("\n=== FORMATOWANIE ===");
Console.WriteLine($"double z 2 miejscami: {liczbaDouble.ToString("F2")}");
Console.WriteLine($"int jako waluta: {liczbaInt.ToString("C")}");
Console.WriteLine($"data w formacie: {data.ToString("yyyy-MM-dd HH:mm")}");
// Użycie interpolacji stringów (najwygodniejsze)
Console.WriteLine($"\nInterpolacja: Liczba {liczbaInt} i data {data:yyyy-MM-dd}");Konwersja string → inne typy
1. Metody Parse() – rzucają wyjątek przy błędzie
Console.WriteLine("=== METODY PARSE ===");
try
{
// Poprawne konwersje
string tekst1 = "123";
string tekst2 = "45.67";
string tekst3 = "true";
string tekst4 = "2024-12-25";
int liczbaZ1 = int.Parse(tekst1);
double liczbaZ2 = double.Parse(tekst2);
bool wartośćZ3 = bool.Parse(tekst3);
DateTime dataZ4 = DateTime.Parse(tekst4);
Console.WriteLine($"'{tekst1}' → int: {liczbaZ1}");
Console.WriteLine($"'{tekst2}' → double: {liczbaZ2}");
Console.WriteLine($"'{tekst3}' → bool: {wartośćZ3}");
Console.WriteLine($"'{tekst4}' → DateTime: {dataZ4}");
}
catch (FormatException ex)
{
Console.WriteLine($"Błąd formatowania: {ex.Message}");
}
catch (OverflowException ex)
{
Console.WriteLine($"Wartość poza zakresem: {ex.Message}");
}
// Przykład błędu
try
{
string niepoprawnyTekst = "abc123";
int liczba = int.Parse(niepoprawnyTekst); // Rzuci FormatException
}
catch (FormatException)
{
Console.WriteLine("Nie można przekonwertować 'abc123' na int!");
}Metody TryParse() – bezpieczne, zwracają bool
Console.WriteLine("\n=== METODY TRYPARSE (BEZPIECZNE) ===");
// Prawidłowe konwersje
string[] testoweTeksty = { "123", "45.67", "abc", "true", "999999999999999999999" };
foreach (string tekst in testoweTeksty)
{
// TryParse dla int
if (int.TryParse(tekst, out int wynikInt))
{
Console.WriteLine($"✅ '{tekst}' → int: {wynikInt}");
}
else
{
Console.WriteLine($"❌ '{tekst}' nie można przekonwertować na int");
}
// TryParse dla double
if (double.TryParse(tekst, out double wynikDouble))
{
Console.WriteLine($"✅ '{tekst}' → double: {wynikDouble}");
}
else
{
Console.WriteLine($"❌ '{tekst}' nie można przekonwertować na double");
}
}
// Praktyczny przykład - pobieranie danych od użytkownika
Console.Write("\nPodaj swój wiek: ");
string inputWiek = Console.ReadLine();
if (int.TryParse(inputWiek, out int wiek) && wiek >= 0 && wiek <= 150)
{
Console.WriteLine($"Twój wiek: {wiek} lat");
if (wiek >= 18)
Console.WriteLine("Jesteś pełnoletni");
else
Console.WriteLine($"Do pełnoletności pozostało: {18 - wiek} lat");
}
else
{
Console.WriteLine("Podano nieprawidłowy wiek!");
}Porównanie metod konwersji string → int:
| Metoda | Obsługa null | Wyjątki | Wydajność | Użycie |
|---|---|---|---|---|
int.Parse() | ❌ Rzuca wyjątek | FormatException, OverflowException | Najszybsza | Gdy pewni jesteśmy, że string jest poprawny |
int.TryParse() | ❌ Zwraca false | Nie rzuca | Szybka | Bezpieczna konwersja, zawsze używaj |
Convert.ToInt32() | ✅ Zwraca 0 | FormatException, OverflowException | Wolniejsza | Konwersje między różnymi typami |
Konwersje char ↔ int (kody ASCII/Unicode)
Console.WriteLine("=== KONWERSJE CHAR ↔ INT ===");
// char → int (kod ASCII/Unicode)
char litera = 'A';
int kodLitery = litera; // Niejawna konwersja: char → int
Console.WriteLine($"'{litera}' ma kod: {kodLitery}");
char cyfra = '5';
int kodCyfry = cyfra;
int wartośćCyfry = cyfra - '0'; // Konwersja znaku cyfry na jej wartość liczbową
Console.WriteLine($"'{cyfra}' ma kod: {kodCyfry}, wartość: {wartośćCyfry}");
// int → char (z kodu na znak)
int kod = 66;
char znakZKodu = (char)kod; // Jawna konwersja: int → char
Console.WriteLine($"Kod {kod} to znak: '{znakZKodu}'");
// Praktyczne przykłady
Console.WriteLine("\nAlphabet z kodów:");
for (int i = 65; i <= 90; i++) // Kody A-Z
{
Console.Write((char)i + " ");
}
Console.WriteLine("\n\nCyfry z kodów:");
for (int i = 48; i <= 57; i++) // Kody 0-9
{
char cyfrZnk = (char)i;
int cyfrWart = i - 48; // lub i - '0'
Console.WriteLine($"Kod {i} → '{cyfrZnk}' → wartość {cyfrWart}");
}Konwersje bool ↔ inne typy
Console.WriteLine("=== KONWERSJE BOOL ===");
// bool → string (zawsze działa)
bool prawda = true;
bool fałsz = false;
Console.WriteLine($"true → string: '{prawda.ToString()}'");
Console.WriteLine($"false → string: '{fałsz.ToString()}'");
// string → bool
string[] testoweBool = { "true", "false", "True", "FALSE", "1", "0", "yes", "no" };
foreach (string tekst in testoweBool)
{
if (bool.TryParse(tekst, out bool wynik))
{
Console.WriteLine($"✅ '{tekst}' → bool: {wynik}");
}
else
{
Console.WriteLine($"❌ '{tekst}' nie można przekonwertować na bool");
}
}
// bool → int (nie ma bezpośredniej konwersji, trzeba użyć Convert)
bool stan = true;
int stanJakoInt = Convert.ToInt32(stan); // true → 1, false → 0
Console.WriteLine($"bool {stan} → int: {stanJakoInt}");
// int → bool (Convert: 0 = false, wszystko inne = true)
int[] liczby = { 0, 1, -1, 42, 999 };
foreach (int liczba in liczby)
{
bool jakoBool = Convert.ToBoolean(liczba);
Console.WriteLine($"int {liczba} → bool: {jakoBool}");
}Konwersje z formatowaniem kulturowym
// Konwersje uwzględniające ustawienia regionalne
double liczba = 1234.56;
// Formatowanie według różnych kultur
var kulturaPL = new System.Globalization.CultureInfo("pl-PL");
var kulturaUS = new System.Globalization.CultureInfo("en-US");
Console.WriteLine("=== FORMATOWANIE KULTUROWE ===");
Console.WriteLine($"Polska: {liczba.ToString("C", kulturaPL)}"); // 1 234,56 zł
Console.WriteLine($"USA: {liczba.ToString("C", kulturaUS)}"); // $1,234.56
// Parsowanie z uwzględnieniem kultury
string polskaLiczba = "1 234,56";
string amerykańskaLiczba = "1,234.56";
if (double.TryParse(polskaLiczba, NumberStyles.Number, kulturaPL, out double wynikPL))
Console.WriteLine($"Polska liczba: {wynikPL}");
if (double.TryParse(amerykańskaLiczba, NumberStyles.Number, kulturaUS, out double wynikUS))
Console.WriteLine($"Amerykańska liczba: {wynikUS}");Obsługa błędów przy konwersji – najlepsze praktyki
1. Zawsze używaj TryParse dla danych od użytkownika
// ❌ Źle - Parse może rzucić wyjątek
public int PobierzWiekŹle()
{
Console.Write("Podaj wiek: ");
return int.Parse(Console.ReadLine()); // Może się crash'ować!
}
// ✅ Dobrze - TryParse z walidacją
public int PobierzWiekDobrze()
{
while (true)
{
Console.Write("Podaj wiek (0-150): ");
string input = Console.ReadLine();
if (int.TryParse(input, out int wiek) && wiek >= 0 && wiek <= 150)
{
return wiek;
}
Console.WriteLine("❌ Nieprawidłowy wiek! Spróbuj ponownie.");
}
}Ćwiczenia praktyczne
Ćwiczenie 1: Kalkulator z konwersjami
class KalkulatorZKonwersjami
{
static void Main()
{
Console.WriteLine("🧮 Kalkulator z automatycznymi konwersjami typów");
while (true)
{
Console.WriteLine("\nPodaj dwie wartości i operację:");
// Pobierz pierwszą wartość
Console.Write("Pierwsza wartość: ");
if (!TryGetNumber(Console.ReadLine(), out double liczba1))
{
Console.WriteLine("❌ Nieprawidłowa pierwsza wartość!");
continue;
}
// Pobierz operację
Console.Write("Operacja (+, -, *, /, %, ^, exit): ");
string operacja = Console.ReadLine();
if (operacja?.ToLower() == "exit")
break;
// Pobierz drugą wartość (jeśli potrzebna)
double liczba2 = 0;
if (operacja != "sqrt")
{
Console.Write("Druga wartość: ");
if (!TryGetNumber(Console.ReadLine(), out liczba2))
{
Console.WriteLine("❌ Nieprawidłowa druga wartość!");
continue;
}
}
// Wykonaj operację i wyświetl wyniki w różnych formatach
double wynik = WykonajOperację(liczba1, liczba2, operacja);
if (!double.IsNaN(wynik))
{
WyświetlWyniki(liczba1, liczba2, operacja, wynik);
}
}
}
static bool TryGetNumber(string input, out double number)
{
// Spróbuj różnych typów konwersji
if (double.TryParse(input, out number))
return true;
// Może to ułamek?
if (input.Contains("/"))
{
string[] części = input.Split('/');
if (części.Length == 2 &&
double.TryParse(części[0], out double licznik) &&
double.TryParse(części[1], out double mianownik) &&
mianownik != 0)
{
number = licznik / mianownik;
return true;
}
}
number = 0;
return false;
}
static double WykonajOperację(double a, double b, string op)
{
return op switch
{
"+" => a + b,
"-" => a - b,
"*" => a * b,
"/" => b != 0 ? a / b : double.NaN,
"%" => b != 0 ? a % b : double.NaN,
"^" => Math.Pow(a, b),
"sqrt" => a >= 0 ? Math.Sqrt(a) : double.NaN,
_ => double.NaN
};
}
static void WyświetlWyniki(double a, double b, string op, double wynik)
{
Console.WriteLine($"\n✅ Wynik: {a} {op} {b} = {wynik}");
Console.WriteLine("📊 Konwersje wyniku:");
// int (jeśli da się bez straty)
if (wynik == Math.Floor(wynik) && wynik >= int.MinValue && wynik <= int.MaxValue)
{
Console.WriteLine($" int: {(int)wynik}");
}
// float
Console.WriteLine($" float: {(float)wynik}");
// decimal (jeśli w zakresie)
if (wynik >= (double)decimal.MinValue && wynik <= (double)decimal.MaxValue)
{
Console.WriteLine($" decimal: {(decimal)wynik}");
}
// string z różnym formatowaniem
Console.WriteLine($" string (2 miejsca): \"{wynik:F2}\"");
Console.WriteLine($" string (naukowy): \"{wynik:E2}\"");
Console.WriteLine($" string (procent): \"{wynik:P1}\"");
// Reprezentacja binarna (dla liczb całkowitych)
if (wynik == Math.Floor(wynik) && wynik >= 0 && wynik <= long.MaxValue)
{
Console.WriteLine($" binarny: {Convert.ToString((long)wynik, 2)}");
Console.WriteLine($" hex: 0x{(long)wynik:X}");
}
}
}