7. diel - LINQ v C# .NET - Revolúcia v dopytovaní
V predchádzajúcom cvičení, Riešené úlohy k 5.-6. lekcii práce s kolekciami v C# .NET, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.
V dnešnom C# .NET tutoriále sa zameriame na technológiu LINQ, ktorá predstavuje súbor nástrojov na dopytovanie sa na dáta. Jedná sa o veľmi revolučnú technológiu, ktorá prácu s akýmikoľvek dátami zjednodušuje a zovšeobecňuje.
Motivácia
Určite sme všetci doteraz pracovali s rôznymi typmi kolekcií rôznym spôsobom. Inak sme hľadali prvok v poli, inak sme čítali dáta z XML súboru a inak by sme hľadali používateľa v databáze. Predstavte si ale, keby existoval jeden unifikovaný spôsob, akým sa na dáta pýtať. Keby sme mohli ten istý dotaz spustiť ako na obyčajnom poli, tak na XML súbore alebo databáze. Asi tušíte, že LINQ nám poskytuje presne taký komfort. Ide o obrovskú abstrakciu, ktorá je vykúpená iba zanedbateľným znížením výkonu a ktorá vyhnala programovanie v C# do nových výšin.
LINQ ako jazyk
LINQ je pomerne sofistikovaná a rozsiahla technológia. Jej názov pochádza z anglického Language INtegrated Query. Ako názov napovedá, jedná sa o dotazovací jazyk, ktorý je integrovaný priamo do syntaxe jazyka C#. Je teda jeho súčasťou a to od C# 3.0 a .NET frameworku 3.5. Od novších verzií beží dokonca na viacerých vláknach, čo zvyšuje efektivitu tejto technológie.
LINQ je veľmi podobný jazyku SQL a je to teda jazyk deklaratívny. Programu oznámime, čo hľadáme a už nás veľmi nezaujíma, akým spôsobom pre nás dáta naozaj vyhľadá. Výhodou integrácie LINQ do C# je syntaktická kontrola dopytov pri preklade programu.
Urobme si malý príklad, než pôjdeme ďalej. Založte si nový projekt, pôjde o konzolovú aplikáciu s menom LINQ. Vytvoríme si jednoduché pole textových reťazcov:
string[] names = {"David", "Martin", "Daniel", "Peter", "John", "Elisa"};
Teraz si pomocou LINQ dotazu z tohto poľa vyberieme tie položky, ktorých dĺžka je väčšia ako 5 písmen. Do programu zapíšme nasledujúci kód:
var query = from n in names where (n.Length > 5) select n;
Dotaz nápadne pripomína SQL, tí ktorí ho poznajú, majú výhodu. Myslím, že SQL nad poľom ste ešte nevolali, že? Hneď si dotaz podrobne popíšeme, najskôr ale dokončíme náš program a to tým, že si výsledok otázky vypíšeme do konzoly:
{CSHARP_CONSOLE}
string[] names = {"David", "Martin", "Daniel", "Peter", "John", "Elisa"};
var query = from n in names
where (n.Length > 5)
select n;
// printing the result
foreach (string name in query)
{
Console.WriteLine(name);
}
Console.ReadKey();
{/CSHARP_CONSOLE}
Výstup programu:
Konzolová aplikácia
Martin
Daniel
Ako vyzerá dotaaz
Vráťme sa k nášmu dotazu, ktorý vyzeral takto:
var query = from n in names where (n.Length > 5) select n;
Znalci SQL budú určite prekvapení, že je dotaz pospiatky. Má to svoje opodstatnenie, ku ktorému dôjdeme.
Najprv určujeme, odkiaľ budeme dáta vyberať, slúži na to kľúčové
slovo from
. Za from
nasleduje premenná, ktorá bude
vo zvyšku dotazu reprezentovať prvok z kolekcie. Ďalej nasleduje kľúčové
slovo in
a samotná kolekcia. Je to podobné ako pri cykle
foreach
. Dotazy sa píšu na niekoľko riadkov, aby boli
prehľadnejšie. To oceníte najmä u tých zložitejších.
Na opodmienkovanie môžeme uviesť riadok s kľúčovým slovom
where
, za ním nasleduje podmienka. Podmienky v tomto prípade
píšeme úplne rovnako, ako sme to robili doteraz.
Na poslednom riadku nasleduje kľúčové slovo select
, pomocou
ktorého určíme, čo vyberáme. Tu vyberáme celý prvok z kolekcie, teda
n
. Rovnako tak by sme ale mohli vybrať napríklad len jeho dĺžku
n.Length
alebo čokoľvek iné.
Kľúčové slovo var
Dotaz ukladáme do premennej typu var
, s týmto typom sme sa
ešte nestretli a vlastne to ani dátový typ nie je. Kľúčové slovo
var
nám umožňuje prenechať výber dátového typu na kompiléri
(rozumajte, že ho za nás priradí C# sám pri preklade). Teoreticky by sme
var
mohli použiť aj inokedy, napr. takto:
var s = "C# will recognize that this is a string and it will assign the string type to the variable s"; var i = 10;
Kód vyššie C# preloží v podstate na toto:
string s = "C# will recognize that this is a string and it will assign the string type to the variable s"; int i = 10;
Kľúčové slovo var
teda umožňuje určiť dátový typ až
pri preklade programu a vlastne nás od dátového typu odtieňuje. V bežných
programoch by bolo var
na ťažkosť, pretože špecifikácia typov
má svoj zmysel. Typy teda budeme písať ďalej a v žiadnom prípade ich
nenahradzujte slovom var
, ako sa to niektorí začiatočníci
úplne chybne naučia.
Kľúčové slovo var
bolo zavedené kvôli LINQ, a to z troch
dôvodov:
- Po prvé, dátové typy dotazov sú pomerne zložité a bolo by komplikované ich vždy explicitne špecifikovať.
- Po druhé, keď zmeníme typ kolekcie, zmení sa aj typ dotazu, čo by vyžadovalo zbytočnú editáciu kódu a znížilo všeobecnosť technológie.
- Po tretie s LINQ prichádzajú tzv. anonymné typy, pri ktorých sa bez
klúčového
var
nezaobídeme, čoskoro sa k nim dostaneme.
Čo si pamätajte je, že var
má svoje miesto hlavne v
dotazoch a v bežnom kóde by sa nemal príliš vyskytovať, aj keď by
sa tam teoreticky dalo použiť.
Všeobecne platí poučka, že var
môžeme použiť v prípade,
že zjednoduší deklaráciu a pritom je stále jasné, akého typu premenná
je. Ukážme si štyri príklady bez var
a s var
:
int a = 10; List<Dictionary<string, string>> dictionaries = new List<Dictionary<string, string>>(); IOrderedQueryable<User> fromNY = from u in db.Users where u.City == "New York" orderby u.Name select u; int b = a;
Pomocou var
môžeme kód upraviť takto:
var a = 10; var dictionaries = new List<Dictionary<string, string>>(); var fromNY = from u in db.Users where u.City == "New York" orderby u.Name select u; int b = a;
Pri prvej premennej nám var
neprinesie žiadne zjednodušenie.
Pri generickom liste generických slovníkov sa jeho použitie naopak oplatí a
keďže z pravej strany priradenia aj vidíme, akého typu je premenná
dictionaries
, var
je tu aj dobrou voľbou. Rovnako by
ale bolo čistejšie napísať na niečo podobné triedu, ukladať kolekcie
kolekcií je skôr zlá praktika. Pri LINQ dotaze je typ zložitý a keby sme
napr. odmazali orderby
, zmenil by sa na
IQueryable<User>
. Takto nemusíme nad typom premýšľať a
ani ho meniť spolu s dotazom. Posledné použitie var
je
odstrašujúce, vôbec z riadka nepoznáme, čo do premennej b
ukladáme.
Ďalšou častou chybou je, že si ľudia myslia, že var
deklaruje premennú dynamického typu, teda že do nej môžeme uložiť, čo
chceme. Nie je tomu tak, typ sa napevno určí pri preklade a počas programu ho
nemožno meniť. Kód nižšie teda nebude fungovať:
// this code will not work var variable = "It contains text now"; variable = 10; // Now it contains a number
Program vyššie vytvorí premennú typu string
a potom spadne,
pretože sa do typu string
snažíme uložiť int
.
Pod pokrievkou
Ako že to celé funguje? Keď sa pozriete na začiatok vášho zdrojového
kódu, uvidíte, že obsahuje nasledujúce using
:
using System.Linq;
Ten je prednačítaný pri všetkých typoch projektov. Skúsme ho
zakomentovať, v tej chvíli nám Visual Studio podčiarkne v dotaze premennú
names
.
LINQ funguje pomocou tzv. providerov, tých je niekoľko
typov a je aj možné si definovať vlastné. My teraz používame LINQ
To Objects, ktorý je implementovaný práve v mennom priestore
System.Linq
a ktorý rozšíri obyčajné kolekcie ako sú napr.
polia a listy o ďalšie metódy navyše. Pod pokrievkou sú teda rozširujúce
metódy.
Skúsme si teraz (using
majme stále ešte zakomentovaný)
vyvolať ponuku metód na našom poli. Napíšme names.
(names
a bodku), aby sme vyvolali zoznam metód na poli:
Teraz using
opäť odkomentujme a urobme to isté znova:
Na obyčajnom poli máme zrazu kvantum nových metód. Keď C# vykonáva LINQ dotaz, volá na pozadí na kolekciu tieto metódy. Tie sú riešené cez lambda výrazy, ktoré sme už stretli v OOP kurze.
Náš dotaz:
var query = from n in names where (n.Length > 5) select n;
C# prežuje a vygeneruje nasledujúci kód:
{CSHARP_CONSOLE}
string[] names = {"David", "Martin", "Daniel", "Peter", "John", "Elisa"};
var query = names.Where(n => n.Length > 5).Select(n => n);
// printing the result
foreach (string name in query)
{
Console.WriteLine(name);
}
Console.ReadKey();
{/CSHARP_CONSOLE}
Výstup:
Konzolová aplikácia
Martin
Daniel
Môžete si vyskúšať, že dotaz bude fungovať rovnako. Máme možnosť s
LINQ pracovať aj takto, ale pomocou SQL-like zápisu je to oveľa
stráviteľnejšie. Mimochodom, práve sme si vysvetlili, prečo je v dotaze
najprv where
a až potom select
. Dáta sa musia najprv
nájsť metódou Where()
a z výsledku sa až potom označia, čo
nás zaujíma metódou Select()
. Dôvodom je teda postupnosť
metód pod pokrievkou technológie.
Na záver si prezraďme, na čo sa v našom prípade preloží onen záhadný
typ var
. Výsledný typ dotazu na našom poli je:
System.Linq.Enumerable.WhereArrayIterator<string>
Keďže nemôžeme z hlavy vedieť, aký typ LINQ práve vráti (presnejšie
povedané by sme od toho mali byť odtienení), bol zavedený typ
var
, ako už bolo povedané vyššie.
V budúcej lekcii, LINQ provideri, anonymné typy, radenie a zoskupovanie, budeme pokračovať v dopytovaní.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 2x (58.52 kB)
Aplikácia je vrátane zdrojových kódov v jazyku C#