Typy proste, zakresy i wartości domyślne

Każda zmienna w C# ma typ, a każdy typ ma swoje granice. Ta lekcja pokazuje, ile miejsca zajmują poszczególne typy, jakie liczby się w nich mieszczą i co się dzieje, gdy się nie zmieszczą. Dowiesz się też, dlaczego 0.1 + 0.2 nie daje dokładnie 0.3.

C# int double decimal 50 min
CEL LEKCJI

Czego się dziś nauczysz

  • Dobierzesz typ do danych, które program ma przechowywać
  • Odczytasz zakres typu za pomocą MinValue i MaxValue
  • Wyjaśnisz, co się dzieje po przekroczeniu zakresu
  • Wskażesz, kiedy użyć double, a kiedy decimal
  • Wyjaśnisz, dlaczego liczb zmiennoprzecinkowych nie porównuje się przez ==
  • Podasz wartość domyślną każdego z poznanych typów
TEORIA

Po co w ogóle typy

Komputer przechowuje wszystko jako ciągi zer i jedynek. Ten sam zestaw bitów może oznaczać liczbę, literę albo kolor piksela — typ mówi, jak te bity odczytać.

Typ decyduje o trzech rzeczach:

Ile miejsca

Zmienna byte zajmuje 1 bajt, int — 4 bajty, double — 8. Więcej miejsca to szerszy zakres, ale i większe zużycie pamięci.

Jakie wartości

W byte zmieści się liczba od 0 do 255 i ani jedna więcej. W bool — tylko prawda albo fałsz.

Co wolno robić

Liczby można dodawać i mnożyć. Tekst można sklejać i mierzyć jego długość. Kompilator pilnuje, żeby nie mylić jednego z drugim.

Dlaczego to dla ciebie dobre

C# sprawdza typy podczas kompilacji. Jeśli spróbujesz odjąć tekst od liczby, dowiesz się o tym natychmiast, w edytorze. W językach bez tej kontroli taki program uruchomi się i zepsuje dopiero u użytkownika — często w najgorszym możliwym momencie.

TEORIA

Typy liczb całkowitych

Liczby bez części ułamkowej: wiek, liczba uczniów, numer indeksu. Jest ich w C# osiem, ale w praktyce używa się głównie dwóch.

TypRozmiarZakresKiedy
byte1 bajt0 … 255bajty pliku, składowa koloru
sbyte1 bajt−128 … 127rzadko
short2 bajty−32 768 … 32 767rzadko
ushort2 bajty0 … 65 535rzadko
int4 bajty−2 147 483 648 … 2 147 483 647domyślny wybór
uint4 bajty0 … 4 294 967 295rzadko
long8 bajtówokoło ±9,2 × 1018gdy int za mały
ulong8 bajtów0 … około 1,8 × 1019rzadko

W praktyce: używaj int

Typy short czy byte oszczędzają pamięć, ale przy dzisiejszych komputerach ta oszczędność jest zwykle bez znaczenia, a kod staje się trudniejszy. Domyślnie bierz int. Sięgnij po long, gdy liczby mogą przekroczyć dwa miliardy — na przykład przy liczeniu milisekund albo bajtów dużego pliku.

Typy bez znaku (uint, ulong) wyglądają kusząco przy wartościach, które nie mogą być ujemne, ale sprawiają kłopoty przy odejmowaniu. Nie używaj ich bez potrzeby.

Przyrostki literałów

Liczba wpisana w kod jest domyślnie typu int. Jeśli chcesz inny typ, dopisujesz literę:

Program.cs
int zwykla = 42;                    // bez przyrostka = int
long duza = 9000000000L;            // L, bo liczba nie mieści się w int
uint bezZnaku = 3000000000U;        // U

// Bez L byłby błąd — kompilator próbowałby zmieścić to w int:
// long zle = 9000000000;   // CS1021: liczba całkowita jest za duża

Co się dzieje po przekroczeniu zakresu

To zachowanie zaskakuje wiele osób. Liczba nie zgłasza błędu — ona się „przekręca”, jak licznik przebiegu w starym samochodzie.

Program.cs
int maksimum = int.MaxValue;         // 2 147 483 647
Console.WriteLine(maksimum);

maksimum = maksimum + 1;             // dodajemy jeden...
Console.WriteLine(maksimum);         // ...i lądujemy na samym dole zakresu
wynik w konsoli
2147483647
-2147483648

Program się nie zatrzyma, tylko poda złą liczbę

To jeden z podstępniejszych błędów: nic nie wybucha, nie ma czerwonego komunikatu, program po prostu liczy dalej na błędnych danych. Dlatego dobierając typ, warto od razu pomyśleć, jak duże wartości mogą się w nim pojawić.

Można kazać C# pilnować przekroczeń słowem checked — wtedy zamiast przekręcenia dostaniesz wyjątek OverflowException. Do wyjątków wrócimy w bloku 8.

TEORIA

Typy liczb z częścią ułamkową

TypRozmiarDokładnośćPrzyrostekDo czego
float4 bajtyokoło 6–9 cyfrfgrafika, gry — gdy liczy się pamięć
double8 bajtówokoło 15–17 cyfrbrakobliczenia domyślnie
decimal16 bajtów28–29 cyfrmpieniądze
Program.cs
float maly = 3.14f;          // f jest konieczne
double zwykly = 3.14;        // bez przyrostka = double
decimal cena = 19.99m;       // m jest konieczne (od ang. money)

// double srednia = 4.5f;    // to zadziała, ale traci sens
// decimal zle = 19.99;      // CS0664 — bez m to literał double

Problem, który musisz znać: 0.1 + 0.2

Program.cs
double a = 0.1 + 0.2;
Console.WriteLine(a);              // 0,30000000000000004
Console.WriteLine(a == 0.3);       // False

decimal b = 0.1m + 0.2m;
Console.WriteLine(b);              // 0,3
Console.WriteLine(b == 0.3m);      // True

Skąd to się bierze

Typy float i double zapisują liczby w systemie dwójkowym. Ułamek 0,1 w systemie dwójkowym jest nieskończony — dokładnie tak, jak 1/3 w systemie dziesiętnym daje 0,3333… Komputer musi gdzieś uciąć, więc zapisuje wartość bardzo bliską 0,1, ale nie dokładnie równą.

Przy jednym działaniu różnica jest niewidoczna. Przy tysiącu działań na kwotach potrafi urosnąć do złotówek.

Typ decimal liczy w systemie dziesiętnym, więc ułamki dziesiętne zapisuje dokładnie. Jest za to wolniejszy i zajmuje dwa razy więcej miejsca.

Zasada nie do podważenia: pieniądze zawsze w decimal

Ceny, salda, kwoty faktur, wypłaty — wszystko, co ktoś potem policzy i porówna ze stanem konta. Użycie double do pieniędzy to klasyczny błąd, który w prawdziwych systemach kosztował już bardzo dużo.

Do wszystkiego innego — pomiarów, średnich, obliczeń fizycznych i geometrycznych — double jest właściwym wyborem.

Jak porównywać liczby z przecinkiem

Skoro double bywa niedokładny, porównanie przez == jest zawodne. Zamiast tego sprawdzamy, czy liczby są dostatecznie blisko siebie:

Program.cs
double a = 0.1 + 0.2;
double b = 0.3;

// ✗ Zawodne
Console.WriteLine(a == b);                        // False

// ✓ Porównanie z tolerancją
double tolerancja = 0.000001;
bool saRowne = Math.Abs(a - b) < tolerancja;
Console.WriteLine(saRowne);                       // True

Math.Abs zwraca wartość bezwzględną, czyli „odległość od zera”. Dzięki temu nie musimy sprawdzać, która liczba jest większa — interesuje nas sama różnica.

TEORIA

Typ znakowy, logiczny i tekstowy

char — pojedynczy znak

Program.cs
char litera = 'A';           // APOSTROFY, nie cudzysłów
char cyfra = '7';            // to znak, nie liczba siedem
char polska = 'ł';
char spacja = ' ';

// Każdy znak ma swój numer w tablicy Unicode
Console.WriteLine((int)litera);      // 65
Console.WriteLine((int)polska);      // 322

Apostrofy kontra cudzysłów

'A' to znak typu char. "A" to tekst typu string, który akurat ma długość jeden. To dwa różne typy i nie można ich mieszać.

W apostrofach musi być dokładnie jeden znak: 'Ab' to błąd kompilacji.

bool — prawda albo fałsz

Program.cs
bool czyPelnoletni = true;
bool czyZdal = false;

// Najczęściej wartość logiczna powstaje z porównania
int wiek = 20;
bool moznaGlosowac = wiek >= 18;      // true

Console.WriteLine(moznaGlosowac);      // True

W C# zero to nie fałsz

W językach C i C++ liczba 0 znaczy „fałsz”, a każda inna „prawda”. W C# tak nie jest. Zapis if (liczba) to błąd kompilacji — warunek musi być typu bool, więc trzeba napisać if (liczba != 0).

To celowe utrudnienie: chroni przed klasyczną pomyłką, w której if (x = 5) zamiast if (x == 5) po cichu zmienia wartość zamiast ją sprawdzić.

string — tekst

Program.cs
string imie = "Anna";
string puste = "";              // tekst o długości zero
string nic = null;              // brak jakiegokolwiek tekstu — to nie to samo!

Console.WriteLine(imie.Length);       // 4
Console.WriteLine(puste.Length);      // 0
// Console.WriteLine(nic.Length);     // wysypie program — nie ma czego mierzyć

string różni się od pozostałych typów z tej lekcji: nie ma stałego rozmiaru i jest typem referencyjnym. Co to znaczy w praktyce, dokładnie wyjaśnimy w bloku 7. Na razie zapamiętaj jedną konsekwencję: tylko string może przyjąć wartość null.

TEORIA

Wartości domyślne

Każdy typ ma wartość, którą przyjmuje, gdy nikt nie podał innej.

TypWartość domyślnaUwaga
int, long, byte, short0
double, float, decimal0
boolfalse
char'\0'znak pusty, nie spacja
stringnullbrak tekstu, a nie tekst pusty

Uwaga: to nie dotyczy zmiennych lokalnych

Wartości domyślne dostają pola klas i elementy tablic. Zmienna lokalna w metodzie nie dostaje nic — i kompilator nie pozwoli jej odczytać, dopóki czegoś do niej nie włożysz. Wracaliśmy do tego w lekcji 04; to błąd CS0165.

Jeśli potrzebujesz wartości domyślnej wprost, służy do tego słowo default:

Program.cs
int liczba = default;          // 0
bool flaga = default;          // false
string tekst = default;        // null

Console.WriteLine(liczba);     // 0
Console.WriteLine(flaga);      // False
UŻYTE ELEMENTY

Co dokładnie robi każdy użyty element

ElementZwracaDziałanie
int.MinValue
int.MaxValue
int Stałe wbudowane w typ. Podają najmniejszą i największą wartość, jaka się w nim mieści. Ma je każdy typ liczbowy: byte.MaxValue, double.MinValue i tak dalej.
Math.Abs(x)ten sam typ co x Wartość bezwzględna — usuwa znak minus. Math.Abs(-7) daje 7. Klasa Math jest statyczna, więc nie tworzy się jej obiektu.
Math.Round(x, n)double lub decimal Zaokrągla x do n miejsc po przecinku.
sizeof(typ)int Podaje, ile bajtów zajmuje typ prosty. sizeof(int) daje 4.
(int)znakint Rzutowanie — nawias z nazwą typu przed wartością każe potraktować ją jako inny typ. Dla char daje numer znaku w tablicy Unicode.
double.IsNaN(x)bool Sprawdza, czy wynik jest „nie-liczbą” (ang. Not a Number) — tak oznaczany jest np. rezultat dzielenia 0,0 przez 0,0.
x.GetType().Namestring Zwraca nazwę typu wartości x. Przydatne, gdy chcesz sprawdzić, co naprawdę wywnioskował kompilator z var.
PRZYKŁAD Z OMÓWIENIEM

Program, który pokazuje granice typów

Program.cs
Console.WriteLine("=== ROZMIARY I ZAKRESY ===");
Console.WriteLine($"byte:   {sizeof(byte)} B   od {byte.MinValue} do {byte.MaxValue}");
Console.WriteLine($"int:    {sizeof(int)} B   od {int.MinValue} do {int.MaxValue}");
Console.WriteLine($"long:   {sizeof(long)} B   od {long.MinValue} do {long.MaxValue}");
Console.WriteLine($"double: {sizeof(double)} B");

Console.WriteLine();
Console.WriteLine("=== PRZEKROCZENIE ZAKRESU ===");
byte licznik = 255;
Console.WriteLine($"licznik = {licznik}");
licznik = (byte)(licznik + 1);
Console.WriteLine($"po dodaniu 1: {licznik}");

Console.WriteLine();
Console.WriteLine("=== DOKLADNOSC ===");
double wynikDouble = 0.1 + 0.2;
decimal wynikDecimal = 0.1m + 0.2m;
Console.WriteLine($"double:  {wynikDouble}");
Console.WriteLine($"decimal: {wynikDecimal}");

Console.WriteLine();
Console.WriteLine("=== CO WYWNIOSKOWAL KOMPILATOR ===");
var a = 42;
var b = 42.5;
var c = 42.5m;
var d = '4';
Console.WriteLine($"42    -> {a.GetType().Name}");
Console.WriteLine($"42.5  -> {b.GetType().Name}");
Console.WriteLine($"42.5m -> {c.GetType().Name}");
Console.WriteLine($"'4'   -> {d.GetType().Name}");
wynik w konsoli
=== ROZMIARY I ZAKRESY ===
byte:   1 B   od 0 do 255
int:    4 B   od -2147483648 do 2147483647
long:   8 B   od -9223372036854775808 do 9223372036854775807
double: 8 B

=== PRZEKROCZENIE ZAKRESU ===
licznik = 255
po dodaniu 1: 0

=== DOKLADNOSC ===
double:  0,30000000000000004
decimal: 0,3

=== CO WYWNIOSKOWAL KOMPILATOR ===
42    -> Int32
42.5  -> Double
42.5m -> Decimal
'4'   -> Char

Omówienie

FragmentDlaczego tak
licznik = (byte)(licznik + 1); Rzutowanie jest tu konieczne. Dodawanie dwóch bajtów daje w C# wynik typu int, a takiego nie da się bez pytania wcisnąć z powrotem do zmiennej byte. Nawias (byte) mówi: „wiem, co robię”.
Wynik 0 zamiast 256 Liczba 256 nie mieści się w bajcie, więc wartość przekręciła się na początek zakresu. Program nie zgłosił żadnego problemu — to właśnie czyni ten błąd niebezpiecznym.
0,30000000000000004 Zapis dwójkowy nie potrafi dokładnie przedstawić ułamka 0,1. Dla decimal, który liczy dziesiętnie, problem nie występuje.
Int32, Double To prawdziwe nazwy typów w bibliotece .NET. int jest skrótem na System.Int32, double na System.Double. Obu zapisów można używać zamiennie, ale w kodzie C# przyjęło się pisać krótkie.
sizeof(double) bez zakresu Zakres typów zmiennoprzecinkowych podaje się przez rząd wielkości, a nie przez dokładne liczby — ich MinValue to gigantyczna wartość ujemna, nieczytelna na ekranie.
CZĘSTE BŁĘDY

Na co uważać

1. Dzielenie liczb całkowitych ucina resztę

Program.cs
int suma = 7;
int ile = 2;

double srednia = suma / ile;             // ✗ 3, a nie 3,5!
Console.WriteLine(srednia);              // 3

double poprawna = (double)suma / ile;    // ✓ 3,5
Console.WriteLine(poprawna);             // 3,5

To najczęstszy błąd w całym bloku podstaw. Dzielenie int przez int daje int — reszta jest odrzucana, zanim wynik trafi do zmiennej double. Wystarczy zamienić na double jedną ze stron, żeby całe działanie policzyło się dokładnie.

2. Zapomniany przyrostek

KodProblem
float x = 3.14;CS06643.14 to literał double, który nie zmieści się w float. Poprawnie: 3.14f.
decimal c = 19.99;CS0664 — potrzebny przyrostek m.
long x = 9000000000;CS1021 — bez L kompilator próbuje zmieścić liczbę w int.

3. Pozostałe pułapki

KodProblem
char c = "A";Cudzysłów zamiast apostrofów — to string, nie char.
if (liczba)W C# warunek musi być typu bool. Poprawnie: if (liczba != 0).
if (a == b) dla doubleKompiluje się, ale bywa zawodne. Porównuj przez Math.Abs(a - b) < tolerancja.
double cena = 19.99; dla pieniędzyDziała, ale po wielu operacjach zgubi grosze. Kwoty trzymamy w decimal.
int wiek = 17.5;CS0266 — typ całkowity nie przyjmie ułamka. Trzeba rzutować i pogodzić się z utratą części ułamkowej.
string s = 'A';CS0029 — pojedynczy znak nie jest tekstem. Poprawnie "A".
ZADANIA

Zadania

ZAD 1Dobierz typ★☆☆

Dla każdej informacji wybierz najodpowiedniejszy typ i uzasadnij wybór: liczba uczniów w klasie, cena produktu w sklepie, ocena z przedmiotu, informacja czy uczeń jest obecny, pierwsza litera nazwiska, liczba mieszkańców Ziemi, numer PESEL.

pokaż rozwiązanie
InformacjaTypDlaczego
liczba uczniów w klasieintLiczba całkowita, mała. byte też by wystarczył, ale int jest wygodniejszy w działaniach.
cena produktudecimalTo pieniądze. Zawsze decimal.
ocena z przedmiotuint lub doubleint dla ocen całkowitych, double jeśli dopuszczamy 4,5.
czy uczeń obecnyboolDwa możliwe stany.
pierwsza litera nazwiskacharDokładnie jeden znak.
liczba mieszkańców ZiemilongPonad 8 miliardów — int kończy się na 2,1 miliarda.
numer PESELstringTo pułapka. Mimo że składa się z cyfr, PESEL to identyfikator, nie liczba. Nie wykonuje się na nim działań, a wiodące zero zniknęłoby w typie liczbowym.

Ostatni punkt to reguła warta zapamiętania: jeśli na czymś nie wykonujesz działań matematycznych, to nie jest liczba — nawet gdy składa się z cyfr. Dotyczy to numerów telefonów, kodów pocztowych i numerów kont.

ZAD 2Tabela zakresów★☆☆

Napisz program, który wypisze rozmiar w bajtach oraz wartość najmniejszą i największą dla typów byte, short, int i long. Sformatuj wynik w kolumny.

pokaż rozwiązanie
Console.WriteLine("Typ       Bajty                     Min                   Max");
Console.WriteLine(new string('-', 62));

Console.WriteLine($"byte      {sizeof(byte),5}{byte.MinValue,24}{byte.MaxValue,22}");
Console.WriteLine($"short     {sizeof(short),5}{short.MinValue,24}{short.MaxValue,22}");
Console.WriteLine($"int       {sizeof(int),5}{int.MinValue,24}{int.MaxValue,22}");
Console.WriteLine($"long      {sizeof(long),5}{long.MinValue,24}{long.MaxValue,22}");

Liczba po przecinku w klamrach to wyrównanie: {sizeof(byte),5} rezerwuje 5 znaków i dosuwa wartość do prawej. Ujemna liczba, na przykład {tekst,-8}, dosuwałaby do lewej.

new string('-', 60) tworzy napis złożony z sześćdziesięciu myślników — wygodniejsze niż przepisywanie ich ręcznie.

ZAD 3Przewidz wynik★★☆

Zapisz na kartce, co wypisze każda linia. Dopiero potem uruchom program i porównaj.

co wypisze?
Console.WriteLine(7 / 2);
Console.WriteLine(7.0 / 2);
Console.WriteLine(7 / 2.0);
Console.WriteLine((double)7 / 2);
Console.WriteLine(7 % 2);
Console.WriteLine(1 / 3 * 3);
pokaż rozwiązanie
3
3,5
3,5
3,5
1
0

Linia 1 — dwie liczby całkowite, więc dzielenie całkowite: reszta odrzucona.

Linie 2, 3, 4 — wystarczy, że jedna strona jest typu double, a całe działanie liczy się dokładnie. Nieważne która.

Linia 5 — operator % zwraca resztę z dzielenia. 7 podzielone przez 2 to 3 i reszty 1.

Linia 6 — najciekawsza. 1 / 3 daje 0 (dzielenie całkowite), a 0 * 3 to nadal 0. Matematycznie wynik powinien wynosić 1. To pokazuje, że kolejność działań ma znaczenie przy typach całkowitych.

ZAD 4Kalkulator średniej★★☆

Napisz program, który przechowuje sumę pięciu ocen (liczba całkowita) i oblicza średnią. Wypisz ją z dokładnością do dwóch miejsc po przecinku. Uważaj na dzielenie całkowite.

pokaż rozwiązanie
int ocena1 = 5;
int ocena2 = 4;
int ocena3 = 3;
int ocena4 = 5;
int ocena5 = 4;

int suma = ocena1 + ocena2 + ocena3 + ocena4 + ocena5;
int liczbaOcen = 5;

double srednia = (double)suma / liczbaOcen;

Console.WriteLine($"Suma ocen: {suma}");
Console.WriteLine($"Srednia:   {srednia:F2}");
Suma ocen: 21
Srednia:   4,20

Bez rzutowania (double) wynik wyniósłby 4 zamiast 4,2 — suma 21 podzielona całkowicie przez 5 daje 4, a reszta przepada.

Format F2 wymusza dwa miejsca po przecinku, dlatego widzimy 4,20, a nie 4,2.

ZAD 5Zobacz przekroczenie zakresu★★★

Napisz program, który pokazuje przekręcenie się wartości dla typu short: wypisz wartość maksymalną, dodaj jeden i wypisz wynik. Następnie wykonaj to samo działanie w bloku checked i opisz, czym się różni zachowanie programu.

pokaż rozwiązanie
short maksimum = short.MaxValue;
Console.WriteLine($"short.MaxValue = {maksimum}");

// Bez kontroli — wartosc sie przekreca
short poDodaniu = (short)(maksimum + 1);
Console.WriteLine($"po dodaniu 1:  {poDodaniu}");

// Z kontrola — program zglasza problem
try
{
    short zKontrola = checked((short)(maksimum + 1));
    Console.WriteLine(zKontrola);
}
catch (OverflowException)
{
    Console.WriteLine("checked wykryl przekroczenie zakresu!");
}
short.MaxValue = 32767
po dodaniu 1:  -32768
checked wykryl przekroczenie zakresu!

Bez checked program liczy dalej na błędnej wartości i nikt się o tym nie dowiaduje. Z checked zgłaszany jest wyjątek OverflowException, który da się przechwycić i obsłużyć.

Konstrukcja try/catch jest tu użyta zaliczkowo — dokładnie omówimy ją w bloku 8. Na razie wystarczy, że rozumiesz ideę: kod w try jest wykonywany, a jeśli coś pójdzie nie tak, program przeskakuje do catch zamiast się wysypać.

W praktyce checked stosuje się rzadko, bo spowalnia obliczenia. Warto go użyć tam, gdzie błędna liczba miałaby poważne konsekwencje — na przykład w rozliczeniach.

ZAD 6Grosz zgubiony i odzyskany★★★

Napisz program, który dodaje wartość 0,01 tysiąc razy — raz w typie double, raz w decimal. Wypisz oba wyniki i porównaj każdy z oczekiwaną wartością 10 za pomocą operatora ==.

pokaż rozwiązanie
double sumaDouble = 0;
decimal sumaDecimal = 0;

for (int i = 0; i < 1000; i++)
{
    sumaDouble += 0.01;
    sumaDecimal += 0.01m;
}

Console.WriteLine($"double:  {sumaDouble}");
Console.WriteLine($"decimal: {sumaDecimal}");
Console.WriteLine();
Console.WriteLine($"double  == 10: {sumaDouble == 10}");
Console.WriteLine($"decimal == 10: {sumaDecimal == 10}");
double:  9,999999999999831
decimal: 10,00

double  == 10: False
decimal == 10: True

To jest właśnie moment, w którym teoria z tej lekcji staje się namacalna. Tysiąc razy po jednym groszu powinno dać równo 10 złotych — a w typie double zabrakło ułamka grosza.

Wyobraź sobie, że to system kasowy zliczający transakcje przez cały dzień. Po tysiącach operacji różnica przestaje być teoretyczna, a raport kasowy nie zgadza się ze stanem kasy.

Pętli for jeszcze nie omawialiśmy — poznasz ją w bloku 3. Tutaj wystarczy wiedzieć, że powtarza ona instrukcję w środku tysiąc razy.

PODSUMOWANIE

Co trzeba zapamiętać

  • Typ decyduje o rozmiarze, zakresie i dozwolonych operacjach.
  • Do liczb całkowitych domyślnie int; long, gdy wartości przekraczają dwa miliardy.
  • Do obliczeń double. Do pieniędzy zawsze decimal z przyrostkiem m.
  • Po przekroczeniu zakresu wartość przekręca się bez ostrzeżenia — program liczy dalej na złych danych.
  • double zapisuje ułamki w systemie dwójkowym, więc 0.1 + 0.2 nie daje dokładnie 0.3.
  • Liczb zmiennoprzecinkowych nie porównuj przez ==, tylko przez Math.Abs(a - b) < tolerancja.
  • Dzielenie int przez int ucina resztę. Rzutuj jedną stronę na double.
  • char zapisujemy w apostrofach, string w cudzysłowie.
  • W C# zero nie znaczy „fałsz” — warunek musi być typu bool.
  • Wartości domyślne dostają pola klas i elementy tablic. Zmienne lokalne nie — te trzeba zainicjalizować samemu.