V minulém díle jsme si poměrně obšírně vysvětlili, co to .NET Framework vlastně je a jak probíhá kompilace aplikace napsané v .NET. Dnes si popíšeme základní konstrukce jazyků VB.NET a C#, ukážeme si některé rozdíly, které v těchto jazycích máme. Ještě před tím si ale něco povíme o .NET assemblies.
Struktura .NET assembly
Ať už budeme programovat okenní nebo konzolovou aplikaci, web či nějakou knihovnu, výsledkem bude jedna či více .NET assemblies, a to s příponou dll či exe. Co vlastně taková assembly obsahuje?
Tyto obrázky jsem převzal z MSDN, protože jsou výstižné. Assembly může být buď single-file (první obrázek), kdy je vše pohromadě, nebo multifile (druhý obrázek), kde assembly tvoří několik souborů. Je nutné si uvědomit, že v druhém případě soubory Util.netmodule a Graphic.bmp nejsou součástí souboru assembly, ale jsou to oddělené soubory, které se k assembly propojují pouze informací v manifestu. Visual Studio standardně vytváří single-file assemblies.
Manifest
Manifest obsahuje mnoho důležitých informací o samotné assembly a jejím obsahu, jmenovitě jméno assembly, číslo verze, výchozí kulturu (jazykové a národní nastavení), strong name (vysvětlíme později), seznam souborů patřících k assembly, informace o datových typech a implementacích jejich metod a informace o všech referencovaných assembly.
Referencované assemblies jsou takové assemblies, které obsahují datové typy, které naše assembly používá, ale sama je nedeklaruje. Určitě sem patří například základní knihovny .NET Frameworku, které obsahují definice vestavěných datových typů a které určitě každá naše assembly bude používat, proto potřebuje mít referencované knihovny, kde jsou tyto typy definovány.
Moduly
Modul je část assembly, která obsahuje kód a definice datových typů. Modul je víceméně zkompilovaný jeden soubor kódu, analogie např. objektových modulů v C/C++. Assembly vznikne slinkováním modulů dohromady linkerem. Samotný modul si relativně jednoduše můžete vytvořit pomocí řádkového kompilátoru csc či vbc, stačí mu dát nějaký přepínač (kompilátory najdete ve složce C:\Windows\Microsoft.NET\Framework\v2.0.50727) a slinkovat moduly dohromady můžete pomocí nástroje al. To je spíš jen tak pro zajímavost, v praxi se o to nemusíme starat, vše za nás udělá vývojové prostředí Visual Studio.
Poznámka: Pokud znáte jazyk Visual Basic, ten má také moduly, což byly ve staré verzi jakési kolekce globálních funkcí, ve VB.NET je modul statická třída. Toto jsou však trochu jiné moduly.
Strong name
Assembly může mít tzv. strong name. Ten je tvořen z názvu assembly, čísla verze, kultury, veřejného klíče a digitálního podpisu. Strong name může vypadat třeba takto:
System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089
Jaké jsou výhody a k čemu to je? V první řadě jde o bezpečnost – assembly je podepsaná pomocí privátního klíče, který má jen vydavatel dané assembly a nikdo jiný. Pokud tedy vaše assembly používá nějakou cizí knihovnu a nareferencujete ji pomocí jejího strong name, zařídí se tím, že se může použít pouze ta konkrétní knihovna v dané konkrétní verzi. Nikdo nemůže podvrhnout svoji vlastní knihovnu, měla by jiný digitální podpis; ani samozřejmě nejde existující a správnou knihovnu změnit, pokud by něco takového nastalo, runtime jednoduše vyhodí chybu, protože digitální podpis nebude souhlasit s obsahem assembly. Strong name assembly jednoznačně identifikuje; pokud mají dvě assemblies silný název různý, nejsou stejné.
Navíc je možné mít na jednom stroji stejné knihovny různých verzí, díky silným názvům je používaná verze knihovny přesně definovávna. Strong names se používají mimo jiné právě v manifestu pro určení referencovaných assemblies.
Programovací jazyky
Nad .NET Frameworkem existuje mnoho různých programovacích jazyků, zdaleka nejpoužívanější jsou ale Visual Basic .NET a C#. Tyto dva jazyky jsou si velmi podobné, přestože mají dost odlišný původ.
Visual Basic .NET
Jazyk BASIC vznikl zhruba v 60. letech minulého století a byl určen především pro výuku programování. Tento starý jazyk neměl funkce ani procedury, řádky se číslovaly a vzniklo mnoho jeho různých větví, z nichž žádná se moc neprosadila. Velké popularity se dočkal QBasic, který byl zaintegrován v MS-DOSu, a který umožňoval psát strukturovaně – uměl procedury, funkce, struktury a datové typy. Pořád to ale byl jazyk interpretovaný, což vyřešil až QuickBasic. Programy v QuickBasicu ale moc “quick” nikdy nebyly.
S nástupem Windows se objevil Visual Basic. Napsat okenní aplikaci pro Windows v C/C++ nebylo právě jednoduché, zatímco Visual Basic umožnil díky svému vývojovému prostředí “naklikat” uživatelské rozhraní a dopsat kód. O mnoho let později tuto myšlenku převzala jiná vývojová prostředí, například Borland Delphi.
Poslední verzí klasického Visual Basicu byla verze Visual Basic 6 z roku 1998. Visual Basic jako takový již docela zaostával za konkurencí, nenabízel mnoho výhod a nebyl právě nejrychlejší. Dosáhl i tak ale velké obliby (zvláště na západě) a mnoho aplikací bylo napsáno právě v něm. Podporoval částečně objektově orientované programování (i když moc možností tam nebylo), cílený byl především na databázové aplikace.
S nástupem platformy .NET se Microsoft rozhodl Visual Basic vzkřísit a vytvořil verzi Visual Basic 7 (což je verze jazyka, ne vývojového prostředí). Tento jazyk přinesl mnoho novinek, mnoho věcí se změnilo. Konečně se v jazyce objevila pořádná a kompletní podpora objektově orientovaného programování, díky .NETu jazyk dostal nepřeberné množství knihovních funkcí a zachována byla samozřejmě i kompatibilita s COM a Windows API voláními. Programy napsané ve VB.NET mohou být díky .NET Frameworku rychlejší, samozřejmě ale záleží na tom, jak je aplikace napsána. V současné době je aktuální verzí jazyka Visual Basic 9, která mimo generických typů a jiných vylepšení podporuje technologii LINQ.
Syntaxe jazyka Visual Basic
Visual Basic vychází z jazyka Basic, který byl určen pro výukové účely. Jeho syntaxe je svým způsobem ojedinělá a kvůli jednoduchosti a srozumitelnosti obsahuje mnoho klíčových slov, začátečníci se v ní výrazně lépe orientují, je to taková skoro strojová angličtina. Všechny názvy jsou case-insensitive, což znamená, že nezáleží na velikosti písmen v klíčových slovech, názvech proměnných, metod a typů.
Konec řádku je signifikantní znak, používá se k oddělování příkazů. Na rozdíl od většiny jazyků na konci příkazů nejsou středníky. Pokud chceme na jeden řádek dát více příkazů, můžeme, stačí je oddělit dvojtečkou. Pokud naopak chceme rozdělit příkaz do více řádků, použijeme sekvenci znaků mezera podtržítko. V nové verzi jazyka Visual Basic 10, která se chystá, se tato sekvence může vynechat, kompilátor podle kontextu pozná, jestli se jedná o konec řádku, nebo o jeden řádek rozdělený na více částí (kdy je možné řádky dělit je přesně definováno ve specifikaci a pravidla jsou to celkem jednoduchá a intuitivní).
Komentáře existují pouze jednořádkové, začínají znakem apostrof a končí společně s koncem řádku.
C#
Na rozdíl od Visual Basicu je C# jazykem velmi mladým a jeho historie je prakticky stejně dlouhá jako historie .NET Frameworku. Tento jazyk byl speciálně navržen pro .NET Framework a i když to tak ze začátku nevypadalo, stal se velmi brzy daleko oblíbenějším než Visual Basic. Má syntaxi velmi podobnou jazykům C/C++ a Java, takže je na něj pro stávající vývojáře v těchto technologiích velmi snadné přejít, nemusí se učit novou syntaxi, protože je velmi podobná.
Syntaxe jazyka C#
Jazyk C# je case-sensitive, záleží v něm na velikosti písmen. Konec řádku není signifikantním znakem, k oddělování příkazů se používají středníky.
Komentáře existují jednořádkové (začínají sekvencí dvou lomítek //) anebo víceřádkové (uzavřené mezi skupinami /* a */).
Poznámka: Protože nad .NET Frameworkem je kromě VB.NET i mnoho jiných jazyků, které nerozlišují velikosti písmen, je velmi vhodné i v case-sensitive jazycích nevytvářet názvy lišící se pouze velikostí písmen; rozhodně alespoň v případě, že jde o názvy, které jsou viditelné navenek a které se budou z vnějšku používat.
Struktura kódu uvnitř assembly
V jazycích VB.NET a C# nejsou globální funkce ani proměnné. Veškeré metody (tedy funkce a procedury) a proměnné musí být uzavřeny uvnitř nějakého datového typu. Datový typem myslíme jednu z těchto položek (ne ve všech mohou být metody a proměnné):
- třída
- rozhraní
- struktura
- výčtový typ (enum)
- delegát
- vestavěný typ
Namespaces
Každý typ je zařazen do nějakého jmenného prostoru (namespace). Jmenné prostory tvoří hierarchické rozdělení typů podle jejich funkce a oblasti použití. Například ve jmenném prostoru System se nachází základní datové typy (třeba Integer, String, DateTime) a třídy (např. třída Math obsahující matematické funkce atp.). Vše, co se týká grafiky, se nachází ve jmenném prostoru System.Drawing; vše, co se týká okenních aplikací, je ve jmenném prostoru System.Windows.Forms a podobně.
Pokud datový typ nezařadíte do žádného jmenného prostoru, automaticky bude zařazen do výchozího namespace v assembly (někdy se mu říká globální namespace).
Namespace VbNet.Tutorials
End Namespace
namespace VbNet.Tutorials
{
}
Všechny datové typy, které uvedeme dovnitř sekce namespace, budou zařazeny ve jmenném prostoru VbNet.Tutorials. V kódu můžeme používat všechny datové typy, které jsou ve stejném jmenném prostoru, nebo které jsou ve výchozím jmenném prostoru.
Pokud tedy uvnitř našeho jmenného prostoru nadefinujeme třídy A a třídy B, budeme se ze třídy A odvolávat na třídu B pouhým napsáním názvu třídy, tedy B. Pokud chceme použít typ z jiného jmenného prostoru, musíme se na něj odvolat celou cestou tohoto prostoru, například VbNet.C, pokud by třída C byla ve jmenném prostoru VbNet.
Psát celý název typu včetně jmenného prostoru je samozřejmě zdlouhavé, proto máme možnost si jmenné prostory naimportovat. To se dělá takto pomocí klíčového slova Imports resp. using, které platí pro celý soubor a píše se hned na začátek.
Imports VbNet
Namespace VbNet.Tutorials
End Namespace
using VbNet;
namespace VbNet.Tutorials
{
}
Pokud jmenný prostor VbNet naimportujeme, můžeme v celém souboru používat typy z tohoto jmenného prostoru, aniž bychom museli uvádět jejich cestu; stačí prostě použít název datového typu.
Základní syntaktické konstrukce
Základní datové typy
V této tabulce máme základní vestavěné datové typy, které nám .NET framework nabízí.
VB.NET |
C# |
.NET typ |
Velikost |
Popis |
SByte |
sbyte |
System.SByte |
8 bitů |
8-bit celé číslo se znaménkem (-128 až 127) |
Byte |
byte |
System.Byte |
8 bitů |
8-bit celé číslo bez znaménka (0 až 255) |
Short |
short |
System.Int16 |
16 bitů |
16-bit celé číslo se znaménkem (-2^15 až 2^15 - 1) |
UShort |
ushort |
System.UInt16 |
16 bitů |
16-bit celé číslo bez znaménka (0 - 2^16 – 1) |
Integer |
int |
System.Int32 |
32 bitů |
32-bit celé číslo se znaménkem (-2^31 až 2^31 – 1) |
UInteger |
uint |
System.UInt32 |
32 bitů |
32-bit celé číslo bez znaménka (0 - 2^32 – 1) |
Long |
long |
System.Int64 |
64 bitů |
64-bit celé číslo se znaménkem (-2^63 až 2^63 – 1) |
ULong |
ulong |
System.UInt64 |
64 bitů |
64-bit celé číslo bez znaménka (0 - 2^64 – 1) |
Single |
float |
System.Single |
32 bitů |
32-bit desetinné číslo podle IEEE 754 (7-8 platných cifer) |
Double |
double |
System.Double |
64 bitů |
64-bit desetinné číslo podle IEEE 754 |
Decimal |
decimal |
System.Decimal |
128 bitů |
128-bit desetinné číslo (96-bit celé číslo a pozice desetinné čárky; 29 platných číslic) |
Boolean |
bool |
System.Boolean |
|
hodnoty true / false |
Char |
char |
System.Char |
16 bitů |
16-bit znak podle Unicode |
- |
- |
System.IntPtr |
32/64 bitů |
Pointer (kvůli práci s Windows API) |
String |
string |
System.String |
|
Textový řetězec (Unicode) |
V jakémkoliv programovacím jazyce můžete pro datové typy použít název ze sloupce .NET typ, pokud si naimportujete jmenný prostor System, nemusíte uvádět celý název. V jazyce VB.NET můžete používat i názvy z 1. sloupce, v C# zase názvy z 2. sloupce (a také se to tak v drtivé většině případů píše, jsou to aliasy pro dané datové typy, aby se alespoň se základními typy pracovalo pohodlně).
Deklarace proměnných
Pokud chceme nadeklarovat proměnnou, musíme tak učinit uvnitř nějakého typu, například uvnitř struktury, třídy, případně uvnitř metody. Ve VB.NET používáme konstrukci Dim proměnná As typ, v C# konstrukci typ proměnná. Můžeme používat jak aliasy datového typu pro daný jazyk, tak i samozřejmě .NET názvy (druhý řádek).
Dim a As Integer
Dim b As System.Boolean
int a;
System.Boolean b;
Názvy proměnných mohou obsahovat kromě číslic, písmen a podtržítka též různé národně specifické znaky, třeba písmena s háčky a čárkami, ovšem to bych nedoporučoval. Je to věc názoru, ale pokud člověk vidí anglická klíčová slova prokládaná českými výrazy s nabodeníčky, vypadá to přinejmenším nesourodě a nekonzistentně. Podle mě je vhodné pojmenovávat proměnné, metody, třídy a vše ostatní anglicky. Hlavní je ale mít jasné konvence a dělat to jakkoliv, ale všude stejně.
Poznámka: Ve starších verzích jazyka Visual Basic (6 a nižší) nebylo deklarování proměnných nutné. Pokud jste proměnnou nedeklarovali, použil se pro ni datový typ Variant, který měl poměrně značnou výkonnostní i paměťovou režii. To v .NET Frameworku není možné, je potřeba striktně definovat každou proměnnou, kterou používáme. Ve starém Visual Basicu bylo možné definovat datové typy proměnné i pomocí speciálních symbolů v názvu, například proměnná s$ byla automaticky typu String. Vřele doporučuji tento přežitek ještě ze starého BASICu nepoužívat, dnes se to obecně považuje za zastaralé a není to v souladu s obecnými zvyklostmi a konvencemi. Stejně taky byste neměli do názvů proměnných dávat prefixy ani suffixy naznačující datový typ. Není to nutné, ale je dobré dodržovat styl pojmenovávání proměnných konzistentní se zbytkem .NET Frameworku. Tato pravidla jsou pěkně popsána například v článku pana Ondřeje Linharta Konvence při psaní zdrojového kódu.
Deklarace metod
Metody mohou být umístěny uvnitř tříd či struktur, samozřejmě mohou mít parametry a návratovou hodnotu. Procedury se deklarují takto:
Sub Procedura(ByVal i As Integer, ByVal b As Boolean)
End Sub
void Procedura(int i, bool b)
{
}
Procedury návratovou hodnotu nemají, ve VB.NET se to naznačuje klíčovým slovem Sub, v C# slovem void místo návratového typu. V závorce za názvem funkce (Funkce) je seznam parametrů a jejich datových typů. Ve VB.NET slovo ByVal není nutné psát, Visual Studio si jej tam doplní automaticky. Co ByVal znamená a jak se předávají argumenty hodnotou a referencí si vysvětlíme příště.
Funkce se deklarují velmi podobně (návratový typ je v C# před názvem funkce, ve VB.NET za seznamem argumentů):
Function Funkce(ByVal i As Integer, ByVal b As Boolean) As Integer
Return 5
End Function
int Funkce(int i, bool a)
{
return 5;
}
K vracení hodnot z funkce se používá klíčové slovo return, za kterým následuje výraz určující návratovou hodnotu. Provedením příkazu return se ukončí i provádění metody, další příkazy za tímto řádkem se nespustí.
Klauzule return se dá použít i v procedurách bez uvedení návratové hodnoty k okamžitému ukončení provádění procedury.
Ve VB.NET můžeme vracet návratovou hodnotu z funkce i tak, že ji přiřadíme do názvu funkce. Tím se provádění funkce neukončí, ale návratová hodnota je již nastavena. Provádění funkce či procedury můžeme ukončit též příkazy Exit Function resp. Exit Sub.
Podmínky
Pro vytváření podmíněných bloků, používáme klíčové slovo If. Podmíněné bloky se liší trochu více jazyk od jazyka, proto pro každý jazyk následuje zvláštní popis.
If podmínka Then
blok_příkazů
ElseIf podmínka Then
blok_příkazů
ElseIf podmínka Then
blok_příkazů
Else
blok_příkazů
End If
If podmínka Then příkazy Else příkazy
Ve VB.NET máme dvě verze struktury podmínek – plnou a zkrácenou. Ve zkrácené verzi (poslední řádek v ukázce) podmínek je na místě příkazy jeden nebo více příkazů oddělených dvojtečkou. Větev Else je nepovinná.
V plné verzi je na místech podmínka nějaký výraz (viz dále) a na místech blok_příkazů je jeden nebo více příkazů (oddělené znakem konce řádku, případně dvojtečkou). Samozřejmě i klauzule If je příkaz, podmínky do sebe můžeme vnořovat. Větve ElseIf a Else jsou nepovinné. U dlouhé verze podmínky musí být za slovem Then konec řádku (jinak by se jednalo o zkrácenou podmínku) a povinný je samozřejmě i řádek End If.
if (podmínka)
blok_příkazů
else if (podmínka)
blok_příkazů
else if (podmínka)
blok_příkazů
else
blok_příkazů
Na místech podmínka se očekává výraz (musí být uzavřen v kulaté závorce), na místech blok_příkazů se očekává buď jeden příkaz, nebo blok více příkazů uzavřený do složených závorek {}. Konce řádků nejsou v C# signifikantní, takže můžete celou podmínku dát na jeden řádek, ovšem nedoporučuje se to, není to příliš přehledné.
Větve else if a else jsou nepovinné, na rozdíl od VB.NET, kde je speciální klíčové slovo ElseIf, je zde stejného významu docíleno vlastně vnořenými podmínkami. Celý kód by se dal naformátovat i takto, ovšem v praxi se to nepoužívá. Za prvním klíčovým slovem else je jeden příkaz, a to podmínka. Protože celá podmínka počínaje druhým slovem if v ukázce je jeden jediný příkaz, není nutné jej ohraničovat do složených závorek.
if (podmínka)
blok_příkazů
else
if (podmínka)
blok_příkazů
else
if (podmínka)
blok_příkazů
else
blok_příkazů
Větví ElseIf resp. else if může být libovolný počet, nemusí být samozřejmě ani jedna. Podmíněný výraz se vyhodnocuje tak, že se vyhodnocují popořadě výrazy na místech podmínka a první, který platí, se provede. Pokud tedy platí první podmínka, provede se první blok příkazů a další podmínky se netestují, tím pádem se další bloky příkazů nespustí. Pokud neplatí ani jedna podmínka a je uvedena větev else, provede se ta. Na místě podmínka může být výraz, o kterých je následující kapitolka.
Výrazy a operátory
Aritmetické operátory
VB.NET |
C# |
Popis |
X + Y |
X + Y |
sčítání |
X - Y |
X - Y |
odčítání |
X * Y |
X * Y |
násobení |
X / Y |
X / Y (X nebo Y desetinné číslo) |
dělení (desetinné) |
X \ Y |
X / Y (X i Y celočíselné typy) |
celočíselné dělení
54 děleno 7 je 7 |
X Mod Y |
% |
zbytek po celočíselném dělení (modulo)
54 modulo 7 je 5 |
|
X++ |
přičtení jedničky po vyhodnocení |
|
X-- |
odečtení jedničky po vyhodnocení |
|
++X |
přičtení jedničky před vyhodnocením |
|
--X |
odečtení jedničky před vyhodnocením |
X ^ Y |
|
umocnění |
X << Y |
X << Y |
bitový posun o Y bitů vlevo |
X >> Y |
X >> Y |
bitový posun o Y bitů vpravo |
Všechny operátory (kromě X++, ++X, X--, --X a Mod) mají i své varianty s přidaným znakem = na konec, které výsledek operace přiřadí pro prvního operandu. Např. X += Y sečte hodnotu Y do X a přiřadí výsledek do X, podobně X *= Y vynásobí vynásobí hodnotu X hodnotou Y a uloží ji do X.
Operátory X++, ++X, X-- a --X, které jsou dostupné jen v jazyce C#, fungují tak, že zvýší či sníží hodnotu proměnné o 1. Pokud jsou použity před proměnnou, nejprve se hodnota proměnné zvýší resp. sníží a pak až se použije. Pokud naopak použijeme tyto operátory za proměnnou, hodnota proměnné se nejprve použije ve výrazu a pak až se zvýší resp. sníží.
Pokud tedy v A je hodnota 3 a napíšeme X = ++A, pak se nejprve hodnota A nastaví na 4 a do X se pak přiřadí hodnota 4. Pokud ale v A je hodnota 3 a napíšeme X = A++, do X se nejprve nastaví hodnota A (tedy 3) a pak až se A zvýší o jedničku (takže v něm bude 4).
Operátory bitového posunu se chovají různě na datových typech se znaménkem a bez znaménka. U typů bez znaménka (UInt16, UInt32, Byte atd.) se nové pozice nalevo doplní nulami (tím pádem posun vlevo o 1 funguje jako násobení dvěma a posun o 1 vpravo funguje jako celočíselné dělení dvěma), u typů se znaménkem (Int32, SByte atd.) se nové pozice nalevo doplní hodnotou původního prvního bitu (takže se při posunech zachovává znaménko, což je první bit). Pokud vás zajímají detaily, podívejte se na článek na Wikipedii o aritmetickém a logickém posunu.
Relační operátory
VB.NET |
C# |
Popis |
X < Y |
X < Y |
je menší |
X <= Y |
X <= Y |
je menší nebo rovno |
X > Y |
X > Y |
je větší |
X >= Y |
X >= Y |
je větší nebo rovno |
X <> Y |
X != Y |
není rovno |
X = Y |
X == Y |
je rovno |
Výsledkem výrazů s relačními operátory je hodnota typu System.Boolean.
Ve VB.NET je porovnávací operátor stejný jako operátor přiřazení. To, jestli se jedná o přiřazení či porovnání, se pozná podle kontextu. Ve VB.NET nelze ve výrazech operátor přiřazení použít, pokud je ve výrazu nalezen operátor =, jedná se vždy o porovnávání. Operátor přiřazení nevrací na rozdíl od C# žádnou hodnotu.
V C# je operátorem přiřazení znak =, kdežto porovnání je operátor ==. Operátor přiřazení navíc vrací hodnotu, pokud ve výrazu použijeme X = Y, vrátí se přiřazovaná hodnota. Toho můžeme využít například zde:
int i;
if ((i = b * 7) == 0) ...
V podmínce nejprve přiřadíme do proměnné i výraz b * 7 a pak otestujeme, zda-li je jeho hodnota rovna nule. Pokud ano, provedou se příkazy podmínky. Zároveň ale v proměnné i už máme uložen výsledek tohoto výraz. Ve VB.NET je nutné toto přiřazení udělat nad podmínku, za slovem If může být pouze výraz a operátor = do výrazu nepatří.
Logické operátory
VB.NET |
C# |
Popis |
X And Y |
X & Y |
bitový AND |
X Or Y |
X | Y |
bitový OR |
X Xor Y |
X ^ Y |
bitový XOR (platí buď X nebo Y, ale ne obě zároveň) |
Not X |
!X |
negace |
X AndAlso Y |
X && Y |
AND (pokud X neplatí, Y se vůbec netestuje) |
X OrElse Y |
X || Y |
OR (pokud X platí, Y se vůbec netestuje) |
První tři operátory se používají především pro bitové operace - fungují nad číselnými typy. Poslední 3 operátory se používají především v podmínkách - poslední dva dělají takzvané zkrácené vyhodnocování, čehož lze velmi dobře využívat.
Zkrácený AND nevyhodnocuje Y, pokud X neplatí a tím pádem i kdyby Y platilo, výsledek by byl stejně False. Obdobně zkrácený OR nevyhodnocuje Y, pokud X platí, protože i kdyby Y neplatilo, výsledek by byl stejně True.
Poznámka: Priority operátorů jsou popsány na těchto stránkách:
Literály čísel a znaků
Pokud někde v kódu uvedeme celé číslo (např. 15), existuje několik pravidel pro určení datového typu této hodnoty. Bere se to jako hodnota typu System.Int32, pokud se do tohoto rozsahu nevejde, bere se jako System.Int64, případně jako System.UInt64. Pokud uvedeme desetinné číslo (např. 15.0), bere se automaticky jako Double.
Pokud chceme ručně říci, jakého typu má číselná konstanta být, uvedeme za číslo příponu identifikující typ. V tabulce jsou uvedeny příklady použití:
VB.NET |
C# |
Datový typ |
15F |
15f |
System.Single |
15R |
15d |
System.Double |
15US |
|
System.UInt16 |
15UI |
15u |
System.UInt32 |
15UL |
15ul |
System.UInt64 |
15S |
|
System.Int16 |
15I |
|
System.Int32 |
15L |
15L |
System.Int64 |
15D |
15m |
System.Decimal |
“a”C |
‘a’ |
System.Char |
Na velikosti písmen u přípon nezáleží, ale L se v C# kvůli přehlednosti píše velké (aby se nepletlo s jedničkou, kompilátor dokonce vyhodí varování), ostatní většinou velkými písmeny. Pro některé typy C# přípony nemá.
Pokud chcete zapsat literál typu System.Char v C#, stačí jej uzavřít jej do apostrofů (pro řetězce se používají uvozovky). Ve VB.NET se jednotlivé znaky zapisují do uvozovek s příponou C.
Výsledky výrazů a konverze typů
Pro konverzi číselných typů mezi sebou lze použít funkci či operátor přetypování. Ve VB.NET k tomuto účelu slouží funkce CInt, CLong, CShort, CByte, CUInt, CULong, CUShort, CSByte, CSng (na typ Single) a CDbl (na typ Double). V C# stačí před přetypovávaný výraz připsat do kulatých závorek název cílového datového typu, je to tzv. operátor přetypování.
Dim i As Integer = CInt(15.786)
int c = (int)15.768;
Co se týče operátorů jako sčítání, odčítání atd., výsledným datovým typem výrazu bývá přesnější ze vstupních datových typů, minimálně ovšem 32bitový. Pokud tedy sčítáme typy Double a Single, výsledkem je Double. Pokud sčítáme dva Int32, výsledkem je Int32. Pokud ale sčítáme dva Byte, výsledkem je Int32 (výsledek je minimálně 32bitový datový typ, procesory s těmito typy pracují rychleji).
V C# tedy nelze napsat byte i = a + b, kde proměnné a a b jsou typu byte, musíme použít přetypování byte i = (byte)(a + b). Ve VB.NET to napsat jde i bez přetypování, protože standardně jsou povoleny tzv. narrowing conversions, což je automatická konverze číselného datového typu na typ s menším rozsahem nebo přesností. Ve VB.NET tedy klidně můžeme přiřadit Integer do Byte či Double do Single, v C# musíme přetypovávat. Pokud převedeme z Double na Single, ztratíme přesnost, pokud přiřadíme z Integer do Byte, ztratíme tím hodnoty vyšších bitů. Opačné konverze (widening conversions) na typ s větším rozsahem nebo přesností jdou v C# i bez přetypování, ve VB.NET pochopitelně také.
Řetězcové literály
Textové řetězce jsou v .NET Frameworku reprezentovány datovým typem (třídou) System.String. Pokud chceme zapsat řetězec jako literál v kódu, uzavřeme jej do uvozovek. Detaily se liší ve VB.NET a v C#.
Ve VB.NET je situace velmi jednoduchá - text ohraničený uvozovkami je brán jako řetězec. Pokud dovnitř řetězce chceme vložit uvozovky, zdvojíme je “”.
Dim s1 As String = "Hello world!" 'Hello world!
Dim s2 As String = "Hello ""fantastic"" world!" 'Hello "fantastic" world!
V C# máme situaci o trochu složitější, ale díky tomu máme více možností. Uvnitř řetězců můžeme používat tzv. escape sekvence. Pokud dovnitř řetězce dáme zpětné lomítko, následující znak bude mít speciální význam:
Sekvence |
Význam |
\' |
Apostrof |
\" |
Uvozovky |
\\ |
Zpětné lomítko |
\0 |
ASCII znak 0 |
\a |
Znak alert |
\b |
Znak backspace |
\f |
Znak form feed |
\n |
Znak new line |
\r |
Znak carriage return |
\t |
Tabulátor |
\v |
Vertikální tabulátor |
Pokud tedy dovnitř řetězce umístíme někam sekvenci \t, místo ní se ve skutečnosti použije znak tabulátor. Pokud chceme do textu vypsat zpětné lomítko, zdvojíme jej atd.
Někdy se nám toto chování nehodí, protože řetězec obsahuje zpětných lomítek mnoho a bylo by to nepřehledné. Před první uvozovky, které zahajují řetězec, můžeme napsat zavináč. Díky tomu se budou escape sekvence uvnitř řetězce ignorovat a uvnitř řetězce mohou být jakékoliv znaky kromě uvozovek. Pokud chceme napsat uvozovky dovnitř takového řetězce, opět je zdvojíme. Řetězec, před kterým je zavináč, však může obsahovat konec řádku, jak ukazuje poslední ukázka.
Tuto ukázku jsem si vypůjčil ze specifikace jazyka C#.
string a = "hello, world"; // hello, world
string b = @"hello, world"; // hello, world
string c = "hello \t world"; // hello world
string d = @"hello \t world"; // hello \t world
string e = "Joe said \"Hello\" to me"; // Joe said "Hello" to me
string f = @"Joe said ""Hello"" to me"; // Joe said "Hello" to me
string g = "\\\\server\\share\\file.txt"; // \\server\share\file.txt
string h = @"\\server\share\file.txt"; // \\server\share\file.txt
string i = "one\r\ntwo\r\nthree";
string j = @"one
two
three";
Operátor pro slučování řetězců je ve VB.NET &, v C# +. Operátor + funguje i ve VB.NET.
For cyklus
Syntaxe cyklů je ve VB.NET téměř stejná jako v předchozích verzích VB, v C# je zase stejná jako v ostatních jazycích z céčkové rodiny.
For i As Integer = 0 To 14 Step 1
blok_příkazů
Next
Ve VB.NET musí být iterační proměnná cyklu nějakého číselného typu. Pokud proměnnou nemáme deklarovanou před cyklem a chceme ji používat pouze uvnitř cyklu a ne už za ním, můžeme za jejím názvem uvést As Integer, čímž proměnnou i nadeklarujeme pouze v rámci daného cyklu. Za rovnítko uvedeme počáteční hodnotu, za slovo To cílovou hodnotu a volitelně můžeme přidat Step krok, kde krok je hodnota, která se přičte k iterační proměnné i po ukončení každého průchodu těla cyklu. Pokud krok neuvedeme, použije se výchozí hodnota, tedy 1.
Tělo cyklu se bude provádět, dokud je i menší nebo rovno koncové hodnotě při kladném kroku, anebo dokud je i větší nebo rovno koncové hodnotě při záporném kroku. Na místě blok_příkazů může být jeden nebo více příkazů.
for (int i = 0; i < 15; i++)
blok_příkazů
V C# máme možnosti o dost širší. V závorce v první části před středníkem může být libovolný příkaz, který se spustí před zahájením prvního průchodu cyklu. Pokud je v něm deklarace proměnné (což nejčastěji bývá), platí proměnná jen v rámci tohoto cyklu. Může tam být ale jakýkoliv příkaz.
Ve druhé části je podmínka, která se před každým průchodem vyhodnotí a pokud neplatí, provádění cyklu skončí. Poslední část je příkaz, který se spustí po každém průchodu (opět nemusíme inkrementovat zrovna i, můžeme dělat cokoliv jiného).
Přestože máme v C# obrovskou volnost v tom, co budeme s for cyklem dělat, důrazně vás varuji před vymýšlením krkolomných cestiček, jak veškerou logiku cyklu nacpat do hlavičky cyklu a nemuset psát tělo. Takové cykly se velmi špatně luští a činí kód nepřehledným. V drtivé většině případů doporučuji používat standardní syntaxi, která je konec konců uvedena i v příkladu - v první části deklarace a inicializace, ve druhé části jednoduchá podmínka, ve třetí inkrementace o zvolený krok. Každému je hned jasné, co cyklus dělá.
Na místě blok_příkazů může být jeden příkaz nebo blok příkazů ohraničený složenými závorkami.
Poznámka: Je zde ještě jeden drobný detail. Pokud ve VB.NET za klíčové slovo To uvedete výraz, vyhodnotí se pouze jednou, a to před spuštěním cyklu, a dále ne. Pokud se jeho hodnota změní během provádění cyklu, nic se neděje a cyklus jede dál. Například pokud budete mít nějakou sadu položek, jejichž počet je třeba v proměnné n, uděláte cyklus od 0 do n - 1 a v některém kroku určité položky smažete, čímž se změní počet, tak přestože proměnná n bude mít hodnotu třeba 23, cyklus klidně poběží do 25, protože hodnota se určila na začátku platí ta. Je třeba použít jiný druh cyklu. V C# tohle už z principu není možné, podmínka se vyhodnocuje vždy, protože může být jakákoliv a vůbec nemusí jít o nějaké porovnávání, může to být třeba volání nějaké funkce.
While cyklus
Cykly while jsou co do funkčnosti v obou jazycích ekvivalentní. Mají následující strukturu:
While podmínka
blok_příkazů
End While
while (podmínka)
blok_příkazů
Na místě podmínka je výraz vracející typ Boolean. Na místě blok_příkazů může být ve VB.NET jeden nebo více příkazů oddělených koncem řádku nebo dvojtečkou, v C# pak buď jeden příkaz, nebo blok příkazů ohraničený složenými závorkami.
Nejprve se vyhodnotí podmínka, pokud platí, provede se tělo cyklu, a tento postup se opakuje. Jakmile podmínka přestane platit (což může být klidně i při prvním otestováním), provádění cyklu končí. Tělo cyklu se tedy nemusí spustit ani jednou.
Do cyklus
Posledním typem cyklů je cyklus do. Ten má ve VB.NET více možností, ovšem většinou se používá jen tak, která je k dispozici v C#, ostatní jsou nahraditelné.
Do
blok_příkazů
Loop While podmínka
Místo klíčového slova While můžeme ve VB.NET použít ještě klíčové slovo Until, přičemž sekce While/Until podmínka může být i za klíčovým slovem Do (dvě podmínky mít cyklus ale nesmí, povolena je jen jedna).
Pokud je specifikováno While, tělo cyklu se opakuje, pokud podmínka platí. Pokud je specifikováno Until, tělo cyklu se opakuje, pokud podmínka neplatí. To, jestli je podmínka uvedena za Do nebo za Loop, určuje, kdy se bude podmínka testovat. Pokud je uvedena za Do, otestuje se před prvním průchodem těla cyklu. Pokud je až za Loop, testuje se až po prvním průchodu. Cyklus Do While … Loop je ekvivalentní předchozímu typu cyklu While, ostatní varianty dělají něco nepatrně jiného. Rozdíl mezi While a Until je jen v negaci podmínky.
do
blok_příkazů
while (podmínka)
V C# je povolena pouze tato konstrukce, která spolu s while cyklem pokrývá všechny případy, které jsou v běžné praxi potřeba.
Navigace v cyklech
Uvnitř cyklů můžeme používat klíčová slova Exit resp. break a continue. Použitím slova Exit / break okamžitě ukončíme provádění daného cyklu. Použitím slova continue přestaneme provádět daný krok a ihned pokračujeme dalším (před tím se otestuje samozřejmě podmínka). Tyto konstrukce můžeme použít v cyklech For, While a Do.
For i As Integer = 0 To 9
If i = 3 Then Continue For
If i = 5 Then Exit For
Next
for (int i = 0; i < 10; i++)
{
if (i == 3) continue;
if (i == 5) break;
}
Pokud máme v sobě více vnořených cyklů, dané break a continue se vztahuje na hierarchicky nejbližší cyklus. Ve VB.NET můžeme pomocí klíčového slova za Exit resp. Continue určit typ cyklu, takže pokud budeme mít For cyklus uvnitř While cyklu, voláním Exit While vyskočíme z vnějšího While cyklu. Pokud máme dva stejné typy cyklů v sobě, platí vždy ten hierarchicky nejbližší, tzn. ten vnitřní.
Rozhodovací struktury
Oba dva jazyky podporují i tzv. rozhodovací struktury. Syntaxe i možnosti se poměrně liší, ale v zásadě umí to samé.
Select Case výraz
Case 15
blok_příkazů
Case Is >= 17
blok_příkazů
Case Else
blok_příkazů
End Select
Hodnota na místě výraz se porovná s hodnotami uvedenými za Case a spustí se první větev, která vyhovuje výsledku. Pokud nevyhovuje žádná větev a existuje větev Case Else (je nepovinná), provede se ta. Za slovem Case může být buď přímo hodnota, nebo podmínka tvaru Is operátor hodnota. To v C# nejde.
switch (výraz)
{
case 15:
case 16:
blok_příkazů;
break;
case 17:
blok_příkazů;
goto case 18;
case 18:
blok_příkazů;
break;
default:
blok_příkazů;
break;
}
V C# to funguje velmi podobně, ale s několika drobnými rozdíly. Za blokem příkazů v každé větvi musí být buď klíčové slovo break, které ukončí provádění struktury, anebo klauzule goto case hodnota, která skočí na danou větev. Jedna z těchto věcí tam ale být musí, není možné “propadnout” se do nižší větve jako v C/C++.
Větev může být i prázdná, v tom případě pokud výraz vyhovuje hodnotě takové větve, použije se větev následující. Pokud je tedy hodnota výrazu v ukázce 15 nebo 16, použije se první blok příkazů. Pokud je rovna 17, provede se druhý blok příkazů a pak ještě třetí blok, na který jsme skočili použitím goto case. Za case nemůže být podmínka, jen konstanty.
Větev default je samozřejmě nepovinná a provede se, pokud neplatila žádná testovaná podmínka. Na místech blok_příkazů je jeden nebo více příkazů (nemusí se uzavírat do složených závorek).
Platnost proměnných
Deklarace proměnných můžeme mít v kódu prakticky kdekoliv, musí to být uvnitř nějakého typu (třídy nebo struktury). Pokud je to přímo v těle třídy či struktury, platí tato proměnná v rámci dané třídy / struktury. Pokud proměnnou nadeklarujeme v metodě, platí pouze v dané metodě. Pokud ji nadeklarujeme v hlavičce for cyklu, platí jen v tom for cyklu (hodnota se zachová přes všechny průchody cyklem). Pokud ji nadeklarujeme uvnitř těla cyklu, bude proměnná platit jen v těle cyklu a na konci každého průchodu cyklem se zruší.
Private Function Funkce(ByVal i1 As Integer) As Integer
Dim i2 As Integer
If i1 = 5 Then
Dim i3 As Integer
For i4 As Integer = 0 To 9
Dim i5 As Integer
Next
End If
End Function
int Funkce(int i1)
{
int i2;
if (i1 == 5)
{
int i3;
for (int i4 = 0; i4 < 10; i4++)
{
int i5;
}
}
}
Kde mají platnost proměnné i1 až i5?
- Proměnná i1 je argumentem metody Funkce, je tedy dostupná v rámci onoho volání metody. Každé volání metody Funkce má svoji proměnnou i1.
- Proměnná i2 je deklarována uvnitř funkce a je na tom stejně jako argument metody.
- Proměnná i3 je deklarována uvnitř větve podmínky, platí tedy v konkrétní větvi podmínky. Pokud bychom přidali větev else, v ní již proměnná platit nebude.
- Proměnná i4 je deklarována v hlavičce for cyklu. Má platnost po celou dobu trvání cyklu.
- Proměnná i5 je deklarována uvnitř těla cyklu, má platnost po dobu trvání jednoho kroku cyklu.
Proměnná i0, kterou v ukázce nemáme a která by byla deklarována na stejné úrovni jako metoda, tedy uvnitř třídy nebo struktury, by byla sdílená pro všechna volání metody Funkce.
Závěrem
V tomto díle jsme si ukázali základní elementy jazyků VB.NET a C#. Příště si budeme povídat podrobněji o datových typech, třídách a dalších věcech, které jsou pro pořádné programování nezbytné.