IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 7 - LINQ v C# .NET - Revoluce v dotazování

V předešlém cvičení, Řešené úlohy k 5.-6. lekci práce s kolekcemi v C# .NET, jsme si procvičili nabyté zkušenosti z předchozích lekcí.

V dnešním C# .NET tutoriálu se zaměříme na technologii LINQ, která představuje soubor nástrojů pro dotazování se na data. Jedná se o velmi revoluční technologii, která práci s jakýmikoli daty zjednodušuje a zobecňuje.

Motivace

Určitě jsme všichni doteď pracovali s různými typy kolekcí různým způsobem. Jinak jsme hledali prvek v poli, jinak jsme četli data z XML souboru a jinak bychom hledali uživatele v databázi. Představte si ale, kdyby existoval jeden unifikovaný způsob, jakým se na data dotazovat. Kdybychom mohli ten samý dotaz spustit jak na obyčejném poli, tak na XML souboru nebo databázi. Asi tušíte, že LINQ nám poskytuje přesně takový komfort. Jedná se o obrovskou abstrakci, která je vykoupena pouze zanedbatelným snížením výkonu a která vyhnala programování v C# do nových výšin.

LINQ jako jazyk

LINQ je poměrně sofistikovaná a rozsáhlá technologie. Její název pochází z anglického Language INtegrated Query. Jak název napovídá, jedná se o dotazovací jazyk, který je integrovaný přímo do syntaxe jazyka C#. Je tedy jeho součástí a to od C# 3.0 a .NET frameworku 3.5. Od novějších verzí běží dokonce na více vláknech, což zvyšuje efektivitu této technologie.

LINQ je velmi podobný jazyku SQL a je to tedy jazyk deklarativní. Programu sdělíme co hledáme a už nás moc nezajímá, jakým způsobem pro nás data opravdu vyhledá. Výhodou integrace LINQ do C# je syntaktická kontrola dotazů při překladu programu.

Udělejme si malý příklad, než půjdeme dál. Založte si nový projekt, půjde o konzolovou aplikaci se jménem LINQ. Vytvoříme si jednoduché pole textových řetězců.

string[] jmena = {"David", "Martin", "Dan", "Petr", "Vratislav", "Eliška"};

Nyní si pomocí LINQ dotazu z tohoto pole vybereme ty položky, jejichž délka je větší než 5 písmen. Do programu zapište následující kód:

var dotaz = from j in jmena
            where (j.Length > 5)
            select j;

Dotaz nápadně připomíná SQL, ti kteří ho znají mají výhodu. Myslím, že SQL nad polem jste ještě nevolali, že? :) Hned si dotaz podrobně popíšeme, nejprve ale dokončeme náš program a to tím, že si výsledek dotazu vypíšeme do konzole:

// výpis výsledku
foreach (string jmeno in dotaz)
{
    Console.WriteLine(jmeno);
}
Console.ReadKey();

Výstup programu:

Konzolová aplikace
Martin
Vratislav
Eliška

Jak vypadá dotaz

Vraťme se k našemu dotazu, který vypadal takto:

var dotaz = from j in jmena
            where (j.Length > 5)
            select j;

Znalci SQL budou jistě překvapeni, že je dotaz pozpátku. Má to své opodstatnění, ke kterému dojdeme.

Nejprve určujeme odkud budeme data vybírat, slouží k tomu klíčové slovo from. Za from následuje proměnná, která bude ve zbytku dotazu reprezentovat prvek z kolekce. Dále následuje klíčové slovo in a samotná kolekce. Je to podobné jako u cyklu foreach. Dotazy se píší na několik řádků, aby byly přehlednější. To oceníte zejména u těch složitějších.

Pro opodmínkování můžeme uvést řádek s klíčovým slovem where, za ním následuje podmínka. Podmínky v tomto případě píšeme úplně stejně, jako jsme to dělali doposud.

Na posledním řádku následuje klíčové slovo select, pomocí kterého určíme co vybíráme. Zde vybíráme celý prvek z kolekce, tedy j. Stejně tak bychom ale mohli vybrat třeba jen jeho délku j.Length nebo cokoli jiného.

Klíčové slovo var

Dotaz ukládáme do proměnné typu var, s tímto typem jsme se ještě nesetkali a vlastně to ani datový typ není. Klíčové slovo var nám umožňuje přenechat výběr datového typu na kompileru (rozumějte, že ho za nás přiřadí C# sám při překladu). Teoreticky bychom var mohli použít i jindy, např. takto:

var s = "C# pozná že toto je string a dá proměnné s typ string";
var i = 10;

Kód výše C# přeloží v podstatě na toto:

string s = "C# pozná že toto je string a dá proměnné s typ string";
int i = 10;

Klíčové slovo var tedy umožňuje určit datový typ až při překladu programu a vlastně nás od datového typu odstiňuje. V běžných programech by bylo var naobtíž, jelikož specifikace typů má svůj smysl. Typy tedy budeme psát dále a v žádném případě je nenahrazujte slovem var, jak se to někteří začátečníci zcela chybně naučí.

Klíčové slovo var bylo zavedeno kvůli LINQ a to ze třech důvodů:

  • Zaprvé jsou datové typy dotazů pomerně složité a bylo by komplikované je vždy explicitně specifikovat.
  • Zadruhé když změníme typ kolekce, změní se i typ dotazu, což by vyžadovalo zbytečnou editaci kódu a snížilo obecnost technologie.
  • Zatřetí s LINQem přichází tzv. anonymní typy, u kterých se bez var neobejdeme, brzy se k nim dostaneme.

Co si pamatujte je, že var má své místo hlavně v dotazech a v běžném kódu by se neměl příliš vyskytovat, i když by se tam teoreticky dalo použít.

Obecně platí poučka, že var můžeme použít v případě, že zjednoduší deklaraci a přitom je stále jasné jakého typu proměnná je. Ukažme si 4 příklady bez var a s var:

int a = 10;
List<Dictionary<string, string>> slovniky = new List<Dictionary<string, string>>();
IOrderedQueryable<Uzivatel> prazane = from u in db.Uzivatele
                                      where u.Mesto == "Praha"
                                      orderby u.Jmeno
                                      select u;
int b = a;

Pomocí var můžeme kód upravit takto:

var a = 10;
var slovniky = new List<Dictionary<string, string>>();
var prazane = from u in db.Uzivatele
              where u.Mesto == "Praha"
              orderby u.Jmeno
              select u;
var b = a;

U první proměnné nám var nepřinese žádné zjednodušení. U generického listu generických slovníků se jeho použití naopak vyplatí a jelikož z pravé strany přiřazení i vidíme jakého typu je proměnná slovniky, var je zde i dobrou volbou. Stejně by ale bylo čistší napsat na něco podobného třídu, ukládat kolekce kolekcí je spíše špatná praktika. U LINQ dotazu je typ složitý a kdybychom např. odmazali orderby, změnil by se na IQueryable<Uzivatel>. Takto nemusíme nad typem přemýšlet a ani ho měnit spolu s dotazem. Poslední užití var je odstrašující, vůbec z řádky nepoznáme co do proměnné b ukládáme.

Další častou chybou je, že si lidé myslí, že var deklaruje proměnnou dynamického typu, tedy že do ní můžeme uložit co chceme. Není tomu tak, typ se napevno určí při překladu a během programu ho nelze měnit. Kód níže tedy nebude fungovat:

// tento kód nebude fungovat
var promenna = "Teď tam je text";
promenna = 10; // Teď tam je číslo

Program výše vytvoří proměnnou typu string a poté upadne, protože se do typu string snažíme uložit int.

Pod pokličkou

Jak že to celé funguje? Když se podíváte na začátek vašeho zdrojového kódu, uvidíte, že obsahuje následující using:

using System.Linq;

Ten je přednačtený u všech typů projektů. Zkusme ho zakomentovat, v tu chvíli nám Visual Studio podtrhne v dotazu proměnnou jmena.

LINQ funguje pomocí tzv. providerů, těch je několik typů a je i možno si definovat vlastní. My nyní používáme LINQ To Objects, který je implementován právě ve jmenném prostoru System.Linq a který rozšíří obyčejné kolekce jako jsou např. pole a listy o další metody navíc. Pod pokličkou jsou tedy rozšiřující metody.

Zkusme si nyní (ještě se zakomentovaným usingem) vyvolat nabídku metod na našem poli. Napišme jmena. (jmena a tečku)., abychom vyvolali seznam metod na poli:

Obyčejné metody na poli v C# .NET - Kolekce a LINQ v C# .NET

Nyní řádek opět odkomentujme a udělejme to samé znovu:

Linq metody na poli v C# .NET - Kolekce a LINQ v C# .NET

Na obyčejném poli máme najednou kvantum nových metod. Když C# vykonává LINQ dotaz, volá na pozadí na kolekci tyto metody. Ty jsou řešené přes lambda výrazy, které jsme již potkali v OOP kurzu.

Náš dotaz:

var dotaz = from j in jmena
            where (j.Length > 5)
            select j;

tedy C# přežvýká a vygeneruje následující kód:

var dotaz = jmena.Where(j => j.Length > 5).Select(j => j);

Výstup:

Konzolová aplikace
Martin
Vratislav
Eliska

Můžete si vyzkoušet, že dotaz bude fungovat stejně. Máme možnost s LINQ pracovat i takto, ale pomocí SQL-like zápisu je to mnohem stravitelnější. Mimochodem, právě jsme si vysvětlili, proč je v dotazu nejprve where a až poté select. Data se musí nejprve najít metodou Where() a z výsledku se teprve poté označí co nás zajímá metodou Select(). Důvodem je tedy posloupnost metod pod pokličkou technologie.

Na závěr si prozraďme, na co se v našem případě přeloží onen záhadný typ var. Výsledný typ dotazu na našem poli je:

System.Linq.Enumerable.WhereArrayIterator<string>

Jelikož nemůžeme z hlavy vědět jaký typ LINQ zrovna vrátí (přesněji řečeno bychom od toho měli být odstíněni), byl zaveden typ var, jak již bylo řečeno výše.

V příští lekci, LINQ provideři, anonymní typy, řazení a seskupování, budeme pokračovat v dotazování.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 578x (23.17 kB)
Aplikace je včetně zdrojových kódů v jazyce C#

 

Předchozí článek
Řešené úlohy k 5.-6. lekci práce s kolekcemi v C# .NET
Všechny články v sekci
Kolekce a LINQ v C# .NET
Přeskočit článek
(nedoporučujeme)
LINQ provideři, anonymní typy, řazení a seskupování
Článek pro vás napsal David Hartinger
Avatar
Uživatelské hodnocení:
283 hlasů
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 se informační technologie naučil na Unicorn University - prestižní soukromé vysoké škole IT a ekonomie.
Aktivity