A LINQ to Objects az IEnumerable<T> és IEnumerable interfészeket megvalósító (foreach ciklussal bejárható) kollekciók esetén működnek. A megvalósítás a System.Linq névtérben kapott helyet, amit alapvetően nem kell használatba vennünk, mert ha egy új osztályt készítünk sablon alapján Visual Studio-ban, akkor alapértelmezetten használatba lesz véve.
A LINQ két szintaxissal is használható: metódusok láncolataként (Lambda szintaxis), amelyeknek feltételeket, paramétereket lambda kifejezésekkel tudunk adni, vagy az úgynevezett Query szintaxissal, ami hasonlít az SQL nyelvre.
Az SQL és a LINQ is végső soron relációs algebrára épül, ami az adatot halmazokban kezeli speciális műveletek segítségével.
Kiválasztás
A legegyszerűbb művelet az, ha ki kell választanunk. Önmagában ez a művelet is hasznos tud lenni, főleg, ha egy komplex objektumból csak egy tulajdonság értékeire vagyunk kíváncsiak. Gyakran azonban szűréssel együtt alkalmazott.
using System.Collections.Generic;
using System.Linq;
using System;
namespace PeldaLinqSelect
{
struct Pelda
{
public int X { get; set; }
public int Y { get; set; }
}
class Program
{
static void Main(string[] args)
{
var elemek = new List<Pelda>
{
new Pelda { X = 10, Y = 20 },
new Pelda { X = 11, Y = 23 },
new Pelda { X = 44, Y = 42 },
new Pelda { X = 7, Y = 1 },
new Pelda { X = 9, Y = 12 },
};
var eredmeny = from elem in elemek
select elem.X;
//ugyan ez, lambda szintaxissal:
var eredmeny2 = elemek.Select(i => i.X);
foreach (var item in eredmeny)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
}
A program kimenete:
10
11
44
7
9
A példa esetén kapásból több dolgot is megfigyelhetünk, elsőnek a lista létrehozását és az objektumok példányosításának egy új, eddig nem ismertetett módját. Ezt a szintaxist Object Initializer-nek nevezzük. Olyan osztályok esetén alkalmazható, amelyeknek van paraméter nélküli konstruktora és legalább egy kívülről is írható (public setterrel rendelkező) tulajdonsága. Az egyes tulajdonságok megadása között vesszőt kell alkalmazni.
A másik szembetűnő dolog a listából az X változók kiszedése és kiírása. Az eredmeny és eredmeny2 változók értéke azonos lesz, csupán a megadásukban van különbség.
Az eredmeny változó a Query szintaxissal lett megadva, míg az eredmeny2 a már ismertetett lambda szintaxissal.
A query szintaxis felépítése:
from [valtozo] in [kollekcio] [muveletek]
Érdemes a változót egyes számban definiálni, míg a kollekciót többes számban. Ez segíti a kódunk olvashatóságát.
Projekció
A kiválasztás (select) művelet alkalmazható egyedi leképezések létrehozására is. Ezt projekciónak, vagy leképezésnek nevezzük. A legegyszerűbb példa, ha egy névtelen osztályba adjuk vissza a leképezés eredményét. Nézzünk erre egy példát.
using System.Collections.Generic;
using System.Linq;
using System;
namespace PeldaLinqSelect2
{
struct Pelda
{
public int X { get; set; }
public int Y { get; set; }
}
class Program
{
static void Main(string[] args)
{
var elemek = new List<Pelda>
{
new Pelda { X = 10, Y = 20 },
new Pelda { X = 11, Y = 23 },
new Pelda { X = 44, Y = 42 },
new Pelda { X = 7, Y = 1 },
new Pelda { X = 9, Y = 12 },
};
var eredmeny = from elem in elemek
select new
{
X2 = elem.X*2,
Y2 = elem.Y*2
};
//ugyan ez, lambda szintaxissal:
var eredmeny2 = elemek.Select(i => new
{
X2 = i.X * 2,
Y2 = i.Y * 2
});
foreach (var item in eredmeny)
{
Console.WriteLine("{0}, {1}", item.X2, item.Y2);
}
Console.ReadKey();
}
}
}
A program kimenete:
20, 40
22, 46
88, 84
14, 2
18, 24
A var kulcsszó különösen ilyenkor jön jól, sőt jelen példában a kód működésképtelen is lenne enélkül. Ennek oka az, hogy a visszatérési típus egy névtelen osztály, amelynek a típusát mi nem tudnánk meghatározni, de a fordító meg tudja és ezáltal hivatkozni is tud rá.
Itt megjegyezném, hogy a névtelen osztályokba projekció lehetőségét csak akkor használjuk, ha egy metóduson belül van rá szükség. Ha metódusokon átívelően akarjuk a projekció eredményét alkalmazni, akkor készítsünk rá egy saját osztályt vagy alkalmazzuk a Tupple típust.
Szűrés
A szűrés és a kiválasztás együtt járó műveletek. Az előző példát továbbgondolva nézzük meg, hogy hogyan tudjuk leszűrni az X változók listáját úgy, hogy csak azokat kapjuk meg, amelyek tíznél nagyobbak.
using System;
using System.Collections.Generic;
using System.Linq;
namespace PeldaLinqWhere
{
struct Pelda
{
public int X { get; set; }
public int Y { get; set; }
}
class Program
{
static void Main(string[] args)
{
var elemek = new List<Pelda>
{
new Pelda { X = 10, Y = 20 },
new Pelda { X = 11, Y = 23 },
new Pelda { X = 44, Y = 42 },
new Pelda { X = 7, Y = 1 },
new Pelda { X = 9, Y = 12 },
};
var eredmeny = from elem in elemek
where elem.X > 10
select elem.X;
//ugyan ez, lambda szintaxissal:
var eredmeny2 = elemek.Where(i => i.X > 10).Select(i => i.X);
foreach (var item in eredmeny)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
}
}
A program kimenete:
11
44
A példából látható, hogy a szűrést a where kulcsszó valósítja meg. Ennek a paramétere egy predikátum, ami ha igaz az adott elemre, akkor az elem kiválasztásra kerül.
Sorba rendezés
Tipikus programozási probléma, hogy az adatokat sorba kellene rendezni egy, vagy több szempont alapján. Erre is lehetőséget biztosít a LINQ. Rendezni tudunk növekvő- és csökkenő sorrendben, illetve több szempont alapján is.
Az előző példánál felállított kódot módosítsuk egy picit. Írjuk ki az adatokat az X változó szerint növekvő sorrendben.
using System;
using System.Collections.Generic;
using System.Linq;
namespace PeldaLinqRendezes1
{
struct Pelda
{
public int X { get; set; }
public int Y { get; set; }
}
class Program
{
static void Main(string[] args)
{
var elemek = new List<Pelda>
{
new Pelda { X = 10, Y = 20 },
new Pelda { X = 11, Y = 23 },
new Pelda { X = 44, Y = 42 },
new Pelda { X = 7, Y = 1 },
new Pelda { X = 9, Y = 12 },
};
var eredmeny = from elem in elemek
orderby elem.X ascending
select elem;
//ugyan ez, lambda szintaxissal:
var eredmeny2 = elemek.OrderBy(i => i.X);
foreach (var item in eredmeny)
{
Console.WriteLine("{0}, {1}", item.X, item.Y);
}
Console.ReadKey();
}
}
}
A program kimenete:
7, 1
9, 12
10, 20
11, 23
44, 42
Mint látható, a rendezést az orderby kulcsszó valósítja meg, amit a tulajdonság neve követ, ami alapján rendezni akarunk. Ezután vagy az ascending, vagy a descending kulcsszónak kell következnie attól függően, hogy növekvő, vagy csökkenő sorrendben szeretnénk az adatokat kiírni. A lambda szintaxis esetén az OrderBy és az OrderByDescending metódusok alkalmazhatóak.
Ha több feltétel szerint szeretnénk rendezni, akkor egyszerűen láncolni kell a kifejezéseket. Ebben az esetben az első rendezési feltétel lesz az elsődleges rendezési szempont. Például, ha az előző példát ki szeretnénk még azzal egészíteni, hogy az Y paraméter szerint pedig csökkenő sorrendben legyenek rendezve az adatok, akkor a következő módon módosulnak a kifejezések:
var eredmeny = from elem in elemek
orderby elem.X ascending
orderby elem.Y descending
select elem;
var eredmeny2 = elemek.OrderBy(i => i.X).OrderByDescending(i => i.Y);