IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

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:

// printing the result
foreach (string name in query)
{
    Console.WriteLine(name);
}
Console.ReadKey();

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:

Obyčajné metódy na poli v C# .NET - Kolekcia a LINQ v C# .NET

Teraz using opäť odkomentujme a urobme to isté znova:

Linq metódy na poli v C# .NET - Kolekcia a LINQ v C# .NET

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:

var query = names.Where(n => n.Length > 5).Select(n => n);

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#

 

Predchádzajúci článok
Riešené úlohy k 5.-6. lekcii práce s kolekciami v C# .NET
Všetky články v sekcii
Kolekcia a LINQ v C# .NET
Preskočiť článok
(neodporúčame)
LINQ provideri, anonymné typy, radenie a zoskupovanie
Článok pre vás napísal David Hartinger
Avatar
Užívateľské hodnotenie:
1 hlasov
David je zakladatelem ITnetwork a programování se profesionálně věnuje 15 let. Má rád Nirvanu, nemovitosti a svobodu podnikání.
Unicorn university David sa informačné technológie naučil na Unicorn University - prestížnej súkromnej vysokej škole IT a ekonómie.
Aktivity