LINQ (Language INtegrated Queries)
Inainte de a intra in detalii pentru LINQ, o sa facem o mica introducere in tipuri functionale. In majoritatea limbajelor OOP se suporta si aceste tipuri, puteti pasa functii la alte functii ca parametri sau sa returnati functii. In C# puteti folosi Func<TIn1, TIn2.., TIn14, TResult> (puteti avea 0 sau pana la 14 tipuri de intrare) pentru a reprezenta o functie care are o lista de parametri de tip TIn1, TIn2.., TIn14 si returneaza TResult, si Action<TIn1, TIn2.., TIn14> care este acealasi lucru doar ca nu returneaza nimic. Ambele tipuri sunt delegate-uri, practic sunt echivalentul in C# a pointerilor de functii. De asemenea, puteti declara functii inline, sau cum se numesc de fapt, functii lambada, ca de exemplu:
e => e.ToString(); // Daca tipul lui e poate fi dedus
(int e) => e.ToString();
(e, f) => e.ToString() + f.ToString(); // Daca tipurile lui e si lui f pot fi deduse
(int e, long f) => {
return e.ToString() + f.ToString();
};
Daca functiile sunt mici si vreti sa le folosti local, folositi functii lambda inline. Prin LINQ se ofera urmatoare metode pentru toate clasele care implementeaza IEnumerable<T>:
- Proiectie/map prin Select(Func<TValue, TResult>), daca aveti un IEnumerable<TValue> si o functie care primeste TValue si returneaza TResult prin Select puteti obtine un IEnumerable<TResult>
- Filtrare prin Where(Func<TValue, bool>), daca aveti un IEnumerable<TValue> si un predicat care primeste TValue si returneaza o valoare booleana prin Where puteti obtine un IEnumerable<TValue> doar cu valorile care satisfac predicatul.
- Sortare prin OrderBy(Func<TValue, TKey>), ThenBy(Func<TValue, TKey>) si variantele pentru ordinea descrescatoare OrderByDescending si ThenByDescending (ThenBy si ThenByDescending apartin de IOrderedEnumerable si sunt folosite dupa OrderBy si OrderByDescending), functia data selecteaza pentru un camp sau o computatie peste TValue dupa care sa fie ordonata enumeratia.
- Extragere a unui element prin First, FirstOrDefault, Last, LastOrDefault pentru a extrage primul, respectiv ultimul element cu emitere de exceptie daca secventa este goala sau returnare de valoare implicita pentru metodele cu OrDefault. Se poate da si un predicat ca la Where pentru a lua primul care satisface predicatul.
- Numarare prin Count(), la fel se poate folosi si un predicat pentru a numara cate elemente satisfac predicatul.
- Omitere si prealuare a unui numar de elemente din colectie prin Skip(int) respectiv Take(int).
- Verificarea unei proprietati cu All(Func<TValue, bool>) si Any(Func<TValue, bool>) pentru a testa daca toate respectiv cel putin un element satisface un predicat.
Exista mai multe metode de atat dar acestea sunt cele mai uzuale. Se pot vedea toate metodele pentru IEnumerable din IDE pentru mai multe informatii.
Toate aceste metode se pot folosi cu method-chaining pentru a transforma date intr-un flux bine definit ca in exemplu.
var list = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Console.WriteLine("The processed list is: ");
foreach (var item in list.Where(e => e % 2 == 0)
.Select(e => e.ToString() + e.ToString())
.OrderBy(e => e.Length)
.ThenByDescending(e => e))
{
Console.Write("{0} ", item);
}
Dupa cum se poate observa, operatiile LINQ sunt modelate ca operatii de baze de date, un lucru care este util pentru ca arata cum diferite concepte in stiinta calculatoarelor, cum ar fi colectiile ca structuri de date, si baze de date pot fi unificate prin prisma programarii functionale.
Ce am prezentat este sintaxa folosind metode, exista si o sintaxa de query pentru LINQ care poate fi utila in anumite cazuri dar se recomanda in general sintaxa de metoade.
// Putem scrie LINQ in sintaxa de metode.
var enumerable = list.Where(e => e % 2 == 0).Select(e => e.ToString() + e.ToString());
// Sau echivalent cu sintaxa de query
var enumerable = from e in Generate(1, 23) where e % 2 == 0 select e.ToString() + e.ToString();
Pentru compilator sintaxa de query este tradusa ca sintaxa de metode si nu reprezinta nicio diferenta intre cele doua, este doar o sintaxa prin care in anumite cazuri este mai usor de explicitat o anumita transformare pe date fata de syntaxa de metode cu method-chaining.