Lekce 4 - Typový systém podruhé - Datové typy v C# .NET
V předešlém cvičení, Řešené úlohy k 1.-3. lekci C# .NET, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
Nyní se na datové typy podíváme více zblízka a vysvětlíme si, kdy který použít. Dnešní lekce bude hodně teoretická, ale o to praktičtější bude ta příští. Na konci lekce si vytvoříme pár jednoduchých ukázek.
C# rozeznává dva druhy datových typů: hodnotové a referenční.
Hodnotové datové typy
Proměnné hodnotového datového typu si dokážeme jednoduše představit. Může se jednat např. o číslo nebo znak. V paměti je jednoduše uložena přímo hodnota a my k této hodnotě můžeme z programu přímo přistupovat. Slovo přímo jsem nepoužil dvakrát jen náhodou. V tomto C# kurzu se budeme věnovat výhradně těmto proměnným.
Celočíselné datové typy
Podívejme se nyní na tabulku všech vestavěných celočíselných
datových typů v .NET. Všimněte si typu int
, který již známe
z minula.
Datový typ | Rozsah | Velikost | .NET typ |
---|---|---|---|
sbyte | –128 až 127 | 8 bitů | System.SByte |
byte | 0 až 255 | 8 bitů | System.Byte |
short | –32 768 až 32 767 | 16 bitů | System.Int16 |
ushort | 0 až 65 535 | 16 bitů | System.UInt16 |
int | –2 147 483 648 až 2 147 483 647 | 32 bitů | System.Int32 |
uint | 0 až 4 294 967 295 | 32 bitů | System.UInt32 |
long | –9 223 372 036 854 775 808 až 9 223 372 036 854 775 807 | 64 bitů | System.Int64 |
ulong | 0 až 18 446 744 073 709 551 615 | 64 bitů | System.UInt64 |
Všechna tato šílená čísla z tabulky si pamatovat nemusíte, ve Visual Studiu vám téměř vždy pomůže nástroj IntelliSense. Když budete psát daný datový typ a počkáte, nástroj nad ním ukáže bublinu:
V dokumentaci dodávané k Visual Studiu případně tyto údaje najdete podrobněji. Do dokumentace se dostanete tak, že napíšete konkrétní datový typ, označíte jej a stisknete F1.
Asi vás napadá otázka, proč máme tolik možných typů pro uložení
čísla. Odpověď je prostá: záleží na jeho velikosti. Čím větší
číslo je, tím více spotřebuje paměti. Pro věk uživatele tedy zvolíme
byte
, protože se jistě nedožije více než 255 let. Představte
si databázi milionu uživatelů nějakého systému. Když zvolíme místo
byte
int
, bude zabírat 4x více místa. Naopak když
budeme mít funkci k výpočtu faktoriálu, stěží nám bude stačit rozsah
integeru, a proto použijeme long
.
Všimněme si, že některé typy začínají na u
. Jsou
téměř stejné jako jejich dvojníci bez u
, jen neumožňují
záporné hodnoty a tím pádem mohou na kladnou část uložit 2x vyšší
hodnotu. Těmto typům se říká unsigned, klasickým
signed.
.NET typ je název dané struktury v .NET knihovnách. My používáme tzv. aliasy, aby byla práce jednodušší. Ve skutečnosti si C# kód:
int a = 10;
přebere jako:
System.Int32 a = 10;
My budeme samozřejmě používat aliasy, od toho tam jsou
Nad výběrem datového typu nemusíte moc přemýšlet a většinou se
používá jednoduše int
. Typ řešte pouze v případě, když
jsou proměnné v nějakém poli (obecně kolekci) a je jich tedy více. Potom
se vyplatí zabývat se paměťovými nároky. Tabulky sem dáváme spíše pro
úplnost. Mezi typy samozřejmě funguje již zmíněná implicitní konverze,
můžeme tedy přímo přiřadit int
do proměnné typu
long
a podobně, aniž něco konvertujeme.
Desetinná čísla
U desetinných čísel je situace poněkud jednodušší, máme na výběr
pouze dva datové typy. Samozřejmě se liší opět v rozsahu hodnoty, dále
však ještě v přesnosti (vlastně počtu des. míst). Typ double
má již dle názvu dvojnásobnou přesnost oproti float
.
Datový typ | Rozsah | Přesnost | .NET typ |
---|---|---|---|
float | +−1.5 * 10−45 až +−3.4 * 1038 | 7 čísel | System.Single |
double | +−5.0 * 10−324 až +−1.7 * 10308 | 15–16 čísel | System.Double |
Vzhledem k tomu, že desetinná čísla jsou v počítači uložena ve dvojkové soustavě, dochází k určité ztrátě přesnosti. Odchylka je sice téměř zanedbatelná, nicméně když budete programovat např. finanční systém, nepoužívejte tyto dat. typy pro uchování peněz, mohlo by dojít k malým odchylkám.
Když do proměnné typu float
chceme dosadit hodnotu přímo ve
zdrojovém kódu, musíme použít sufix F
, u double
sufix D
(u double
ho můžeme vypustit, jelikož je
výchozí desetinný typ):
float f = 3.14F; double d = 2.72;
Jako desetinný separátor používáme ve zdrojovém kódu vždy tečku, nehledě na to, jaké máme ve Windows regionální nastavení.
Další vestavěné datové typy
Podívejme se na další datové typy, které nám .NET nabízí:
Datový typ | Rozsah | Velikost/Přesnost | .NET typ |
---|---|---|---|
char | U+0000 až U+ffff | 16 bitů | System.Char |
decimal | +–1.0 * 10−28 až +–7.9 * 1028 | 28–29 čísel | System.Decimal |
bool | true nebo false | 8 bitů | System.Boolean |
char
Typ char
nám reprezentuje jeden znak, na rozdíl od typu
string
, který reprezentoval celý řetězec. Znaky v C# píšeme
do apostrofů.
Apostrof '
píšeme na české klávesnici pomocí
Shift a klávesy vedle Enter (u některých klávesnic je
to klávesa nad Enter, u jiných před ním):
Deklarujme si tedy proměnnou typu char
obsahující nějaký
znak:
char c = 'A';
Typ char
patří v podstatě do celočíselných proměnných
(obsahuje číselný kód znaku), ale je logičtější uvést ho zde. Typ
char
nám vrací např. metoda Console.ReadKey()
.
decimal
Typ decimal
řeší problém ukládání desetinných čísel v
binární podobě, ukládá totiž číslo vnitřně podobně jako text.
Používá se tedy pro uchování peněžních hodnot. Ke všem dalším
matematickým operacím s des. čísly použijeme float
nebo
double
. K zápisu decimal
hodnoty opět používáme
sufix, a sice m
:
decimal m = 3.14159265358979323846m;
bool
Datový typ bool
nabývá dvou hodnot: true
(pravda) a false
(nepravda). Budeme ho používat zejména tehdy,
až se dostaneme k podmínkám. Do proměnné typu bool
lze uložit
jak přímo hodnotu true
/false
, tak i logický výraz.
Zkusme si jednoduchý příklad:
{CSHARP_CONSOLE}
bool b = false;
bool vyraz = (15 > 5);
Console.WriteLine(b);
Console.WriteLine(vyraz);
Console.ReadKey();
{/CSHARP_CONSOLE}
V ukázce jsme použili znak >
. Znaky
>
a <
píšeme na české klávesnici pomocí
Pravého ALT a následujících kláves:
Výstup programu:
Konzolová aplikace
False
True
Výrazy píšeme do závorek. Vidíme, že výraz nabývá hodnoty
true
(pravda), protože 15
je opravdu větší než
5
. Od výrazů je to už jen krok k podmínkám, na které se
podíváme příště.
Referenční datové typy
K referenčním typům se dostaneme až u objektově orientovaného
programování, kde si také vysvětlíme zásadní rozdíly. Zatím budeme
pracovat jen s tak jednoduchými typy, že rozdíl nepoznáme. Zatím se
spokojíme se znalostí, že referenční typy jsou složitější než ty
hodnotové. Jeden takový typ již známe, je jím string
. Možná
vás napadá, že string
nemá nijak omezenou délku. Je to tím,
že s referenčními typy se v paměti pracuje jinak.
Typ string
má na sobě řadu opravdu užitečných metod.
Některé si teď probereme a vyzkoušíme:
String
StartsWith()
,
EndsWith()
a Contains()
Pomocí těchto metod se můžeme jednoduše zeptat, zda řetězec začíná
či končí určitým podřetězcem (substringem) nebo zda řetězec substring
obsahuje. Podřetězcem myslíme část původního řetězce. Všechny tyto
metody budou jako parametr brát samozřejmě podřetězec a vracet hodnoty typu
bool
(true
/false
). Zatím na výstup
neumíme reagovat, ale pojďme si ho alespoň vypsat:
{CSHARP_CONSOLE}
string s = "Krokonosohroch";
Console.WriteLine(s.StartsWith("krok"));
Console.WriteLine(s.EndsWith("hroch"));
Console.WriteLine(s.Contains("nos"));
Console.WriteLine(s.Contains("roh"));
Console.ReadKey();
{/CSHARP_CONSOLE}
Výstup programu:
Konzolová aplikace
False
True
True
False
Vidíme, že vše funguje podle očekávání. První výraz samozřejmě neprošel díky tomu, že řetězec ve skutečnosti začíná velkým písmenem.
ToUpper()
a ToLower()
Rozlišování velkých a malých písmen může být někdy na obtíž.
Mnohdy se budeme potřebovat zeptat na přítomnost podřetězce tak, aby
nezáleželo na velikosti písmen. Situaci můžeme vyřešit pomocí metod
ToUpper()
a ToLower()
, které vrací řetězec ve
velkých a v malých písmenech. Uveďme si reálnější příklad, než je
Krokonosohroch. V proměnné budeme mít řádek konfiguračního souboru,
který psal uživatel. Jelikož se na vstupy od uživatelů nelze spolehnout,
musíme se snažit eliminovat možné chyby, zde např. s velkými písmeny.
{CSHARP_CONSOLE}
string konfig = "Fullscreen shaDows autosave";
konfig = konfig.ToLower();
Console.WriteLine("Poběží hra ve fullscreenu?");
Console.WriteLine(konfig.Contains("fullscreen"));
Console.WriteLine("Budou zapnuté stíny?");
Console.WriteLine(konfig.Contains("shadows"));
Console.WriteLine("Přeje si hráč vypnout zvuk?");
Console.WriteLine(konfig.Contains("nosound"));
Console.WriteLine("Přeje si hráč hru automaticky ukládat?");
Console.WriteLine(konfig.Contains("autosave"));
Console.ReadKey();
{/CSHARP_CONSOLE}
Výstup programu:
Konzolová aplikace
Poběží hra ve fullscreenu?
True
Budou zapnuté stíny?
True
Přeje si hráč vypnout zvuk?
False
Přeje si hráč hru automaticky ukládat?
True
Vidíme, že jsme schopni zjistit přítomnost jednotlivých slov v řetězci tak, že si nejprve řetězec převedeme celý na malá písmena (nebo na velká) a potom kontrolujeme přítomnost slova jen malými (nebo velkými) písmeny. Takto by mimochodem mohlo opravdu vypadat jednoduché zpracování nějakého konfiguračního skriptu.
Trim()
, TrimStart()
a TrimEnd()
Problémem ve vstupech od uživatele může být i diakritika. C# ale naštěstí pracuje plně v UTF-8, nestane se nám tedy, že by se diakritika nějak zkomolila. Další nástrahou mohou být mezery a obecně všechny tzv. bílé znaky, které nejsou vidět, ale mohou nám uškodit. Obecně může být dobré tzv. trimovat všechny vstupy od uživatele. Můžeme trimovat buď okolo celého řetězce, nebo jen bílé znaky před ním a za ním. Prozradíme, že při parsovacích funkcích C# trimuje zadaný řetězec automaticky, než s ním začne pracovat. Odstraněny jsou pouze neviditelné znaky okolo řetězce, např. mezery mezi slovy zůstanou. Zkuste si v následující aplikaci před číslo a za číslo zadat několik mezer:
{CSHARP_CONSOLE}
Console.WriteLine("Zadejte číslo:");
string s = Console.ReadLine();
Console.WriteLine("Zadal jste text: " + s);
Console.WriteLine("Text po funkci trim: " + s.Trim());
int a = int.Parse(s);
Console.WriteLine("Převedl jsem zadaný text na číslo parsováním, zadal jste: " + a);
Console.ReadKey();
{/CSHARP_CONSOLE}
Replace()
Asi nejdůležitější metodou na string
je nahrazení určité
jeho části jiným textem. Jako parametry zadáme dva podřetězce: jeden,
který chceme nahrazovat, a druhý, kterým první chceme nahradit. Metoda
vrátí nový string
, ve kterém proběhlo nahrazení. Když daný
podřetězec metoda nenajde, vrátí původní řetězec.
V následující ukázce použijeme v textu mřížku
#
. Ta se na české klávesnici píše pomocí
Pravého Alt a klávesy X:
A teď si již zkusme nahradit podřetězec metodou
Replace()
:
{CSHARP_CONSOLE}
string s = "Java je nejlepší!";
s = s.Replace("Java", "C#");
Console.WriteLine(s);
Console.ReadKey();
{/CSHARP_CONSOLE}
Výstup programu:
Konzolová aplikace
C# je nejlepší!
Format()
Format()
je velmi užitečná metoda, která nám umožňuje
vkládat do samotného textového řetězce zástupné značky. Ty jsou
reprezentovány jako číslo ve složených závorkách. Prvním číslem je
0
. Jako další parametry metody následují v tomto pořadí
hodnoty, které se mají do textu místo značek vložit. Všimněme si, že se
metoda nevolá na konkrétní proměnné (přesněji instanci, viz další
lekce), ale přímo na typu string
.
V následující ukázce použijeme složené závorky (levou a pravou). Na české klávesnici je píšeme pomocí Pravého Alt a klávesy B pro levou, resp. N pro pravou:
Zkusme si tedy použít metodu Format()
pro vložení několika
proměnných do proměnné typu string
:
{CSHARP_CONSOLE}
int a = 10;
int b = 20;
int c = a + b;
string s = string.Format("Když sečteme {0} a {1}, dostaneme {2}", a, b, c);
Console.WriteLine(s);
Console.ReadKey();
{/CSHARP_CONSOLE}
Výstup programu:
Konzolová aplikace
Když sečteme 10 a 20, dostaneme 30
Konzole sama umí přijímat text v takovémto formátu, můžeme tedy napsat:
{CSHARP_CONSOLE}
int a = 10;
int b = 20;
int c = a + b;
Console.WriteLine("Když sečteme {0} a {1}, dostaneme {2}", a, b, c);
Console.ReadKey();
{/CSHARP_CONSOLE}
Toto je velmi užitečná a přehledná cesta, jak sestavovat řetězce, a
určitě se ji vyplatí mnohdy použít místo běžné konkatenace pomocí
operátoru +
, pokud nebazírujeme na vysoké rychlosti.
Interpolace řetězců
Pokud chceme do řetězce v C# na určitá místa vložit nějaké
proměnné, můžeme kromě zástupných znaků {0}
apod. zapsat
proměnné i přímo do řetězce obalením názvu proměnné do složených
závorek. Takový řetězec poté musíme předsadit znakem $
, aby
C# věděl, že ve složených závorkách má očekávat proměnné a nejedná
se o běžný text:
int a = 10; int b = 20; int c = a + b; Console.WriteLine($"Když sečteme {a} a {b}, dostaneme {c}"); Console.ReadKey();
Znak $
píšeme na české klávesnici pomocí
Pravého ALT a klávesy ů:
Výstup programu:
Konzolová aplikace
Když sečteme 10 a 20, dostaneme 30
Tato funkcionalita je podporována pouze v nových verzích C# a zatím ji nepodporuje náš online kompiler.
Podobně můžeme místo proměnné vložit i výraz. Takováto funkcionalita se nám často hodí při výpisu. Zkusme si také rovnou sečíst čísla přímo ve výrazu:
int a = 10; int b = 20; Console.WriteLine($"Když sečteme {a} a {b}, dostaneme {a + b}"); Console.ReadKey();
Výstup programu:
Konzolová aplikace
Když sečteme 10 a 20, dostaneme 30
PadLeft()
a
PadRight()
Jako poslední si zmíníme metody, které nám k textu naopak mezery
přidají K čemu to je
dobré? Představme si, že máme 100 proměnných a budeme je chtít
uspořádat do tabulky. Text upravíme pomocí metody PadRight()
s
parametrem šířky sloupce, tedy např. 20 znaků. Pokud bude mít text jen 12
znaků, vypíše se za něj 8 mezer, aby měl velikost 20. Obdobně metoda
PadLeft()
by vypsala 8 mezer před něj. Jelikož zatím nemáme
znalosti k vytvoření takové tabulky, budeme si metody jen pamatovat a
vyzkoušíme si je dále v C# kurzu.
Vlastnost Length
Poslední, ale nejdůležitější vlastnost (pozor, ne metoda) je
Length
, tedy délka. Vrací celé číslo, které představuje
počet znaků v řetězci. Za vlastnosti nepíšeme závorky, protože nemají
parametry.
{CSHARP_CONSOLE}
Console.WriteLine("Zadejte vaše jméno:");
string jmeno = Console.ReadLine();
Console.WriteLine("Délka vašeho jména je: {0}", jmeno.Length);
Console.ReadKey();
{/CSHARP_CONSOLE}
Je toho ještě spoustu k vysvětlování a existují další datové typy, které jsme neprobrali.
V následujícím cvičení, Řešené úlohy ke 4. lekci C# .NET, si procvičíme nabyté zkušenosti z předchozích lekcí.