Minule jsme si představili technologii LINQ, v tomhle díle si ukážeme, jak se změnil jazyk C#, aby nám usnadnili dotazování s LINQem. Žádná z nově přidaných možností jazyka není pro použití LINQ nezbytně nutná, jedná se hlavně o syntaktický cukr pro usnadnění dotazování.
Anonymní metody
Anonymní metody byly přidány už do C# 2.0. Anonymní metody nám dovolují definovat funkce přímo tam, kde je potřebujeme. Pro snazší pochopení pomůže ukázka.
Kód bez anonymních metod:
static void Main(string[] args)
{
var query = Orders.Where(Predicate);
}
private static bool Predicate(Order o)
{
return o.Price > 1000;
}
Pro usnadnění přibyly anonymní metody.
var query = Orders.Where(delegate(Order o) { return o.Price > 1000; });
Kompilátor si signaturu funkce vygeneruje sám.
Lambda výrazy
Lambda výrazy zkracují zápis Anonymní metody. Zápis pomocí lambda výrazu vypadá takto:
var query = Orders.Where(o => o.Price > 1000); //Lambda výraz s jedním parametrem
var query = Orders.Where((o, i) => o.Price > i); //Lambda výraz se dvěma parametry
Extension methods
Extension methods nám dovolují přidat metodu k existujcímu datovému typu a tím ho rozšířit. Na pozadí fungují tak, že instanci typu, který mají rozšířit, přejímají jako první parametr. Extension methods nemůžou přistupovat k private a protected prvkům daného typu. Musí být ve statických třídách a musí být statické, datový typ, který rozšiřují, je uveden jako první parametr s klíčovým slovem this.
static void Main(string[] args)
{
Random rand = new Random();
int phoneNumber = rand.NextTelephoneNumber();
}
public static class RandomExtensions
{
public static int NextTelephoneNumber(this Random random)
{
List<int> prefixs = new List<int> { 736, 775, 604, 605, 608, 730 };
int prefix = prefixs[random.Next(0, prefixs.Count)];
return prefix * 1000000 + random.Next(100000, 1000000);
}
}
Právě na Extension methods si LINQ hodně zakládá, hodně to zpřehledňuje kód. Právě díky nim máme pocit, že metody LINQu jsou přímo metodami instance.
IList<Order> orders = new List<Order>();
var countOfOrdersWithPriceOverThousand = orders.Count(o => o.Price > 1000);
Anonymní datové typy
Možná jste si toho všimli už v prvním díle. Enumerátor jakého datového typu je v query?
var query = from o in Orders join c in Customers on o.CustomerID equals c.ID
where o.Price > 1000
select new {c.Name, c.Address, o.Price};
Jedná se anonymní datový typ, nejedná se o dynamický typ, jako je dynamic, pouze nikde neuvádíme jeho definici, opět ji za nás dodělá kompilátor. Zvláštní je jaké dává kompilátor jména, při pohledu Reflektorem můžeme vidět jména typů jako '<>f__AnonymousType1`3'<int, int, decimal>. Kompilátor generuje anonymní typy jako generické a pak jejich typy parametrizuje, aby předcházel vytváření zbytečného množství datových typů.
Properities anonymního datového typu můžeme i pojmenovávat:
var query = from o in Orders join c in Customers on o.CustomerID equals c.ID
where o.Price > 1000
select new {CustomerID = c.ID, OrderID = o.ID, c.Name, c.Address, o.Price};
foreach (var item in query)
{
Console.WriteLine("{0} {1} {2} {3} {4}",
item.CustomerID,
item.OrderID,
item.Name,
item.Address,
item.Price);
}
Implicitně typovaná lokální proměnná
Anonymní datové typy by byly k ničemu, kdybychom je neměli jak použít. Proto se C# 3.0 přišlo také klíčové slovo var. Pokud použijeme var, tak za nás kompilátor sám doplní datový typ, pokud ho dokáže jednoznačně určit z výrazu. Datový typ se určuje pouze jednou, pak ho nelze změnit. Následující kód je neplatný!
var count = 5; //datový typ se implicitně určí jako int
count = "pět stránek"; //tohle nelze, count je typu int
Správné použití var:
foreach (var item in query)
{
Console.WriteLine("Name: {0}\tAdrress: {1}\t\tPrice: {2}", item.Name, item.Address, item.Price);
}
V tomhle případě datový typ položky item neznáme, proto necháme kompilátor, aby ho za nás doplnil. Intellisense pro anonymní datové typy, i implicitně definované typy, funguje. Silná typovost je zachována.
Dotazovací výrazy
Dotazovací výrazy nám umožňují zapisovat LINQ dotazy, tak aby se co podobali SQL syntaxi. Existuje sada klíčových slov from, in, where, let, join, on, equals, into, orderBy, descending, ascending, select, group by. Jedná se o pouhý syntaktický cukr, kompilátor přeloží klauzule na posloupnost volání LINQ metod.
var query = from c in customerList
where c.City == "London"
select c;
//se přeloží jako
var query = customerList.Where(c => c.City == "London");
var custSuppliers =
from cust in customerList
join sup in supplierList on cust.Country equals sup.Country into ss
from s in ss.DefaultIfEmpty()
select new
{
CompanyName = cust.CompanyName,
SupplierName = s == null ? string.Empty : s.SupplierName,
Country = s == null ? string.Empty : s.City,
};
//se přeloží jako
var custSuppliers = customerList.GroupJoin(supplierList, cust => cust.Country, sup => sup.Country, (cust, ss) => new {cust, ss})
.SelectMany(t => t.ss.DefaultIfEmpty(), (t, s) => new
{
CompanyName = t.cust.CompanyName,
SupplierName = s == null ? string.Empty : s.SupplierName,
Country = s == null ? string.Empty : s.City,
});
Dalším významným rozšířením je yield return, ten bude popsán v příštím díle.