Zjišťovat znalosti Linqu u pracovního pohovoru může být obtížné - s Linqem se asi setkal každý C# programátor, ale vždy záleží, do jaké hloubky se s touto technologií seznámil a jestli je si vědom některých záludností - a to nemusí být až tak snadné zjistit.
Nicméně stačí dát uchazeči pár příkladů a lze tak alespoň zjistit základní znalosti.
var numbers = new List<int> { 1,2,4,5,6};
//let's get all even numbers
var evenNumbers = numbers.Select(n => n % 2 == 0);
//add another number to the list
numbers.Add(8);
//check if there has been a change in the even numbers
if (numbers.Select(n => n % 2 == 0).Count() != evenNumbers.Count())
Console.WriteLine("Collection changed");
else
Console.WriteLine("No change in collection");
Samozřejmě, že se vypíše, že kolekce se nezměnila - což může v případě nedokonalé znalosti Linqu leckoho překvapit, vždyť jsme hned z počátku získali kolekci sudých čísel, poté jsme nějaké sudé číslo přidali a přesto kód to vyhodnotí, že k žádné změně nedošlo.
Zákeřnost se skrývá v podstatě Linqu - tam, kde si programátor snad myslel, že získává kolekci sudých čísel, došlo pouze k naprogramování kódu, který umí sudá čísla z kolekce získat. Projevila se tady vlastnost "deferred execution" většiny metod Linqu. V cheatsheetu pro Linq (ke stažení zde) jsou takové metody graficky odděleny od těch, co se provedou okamžitě. Podobný příklad dokáže u pohovoru odhalit, zda uchazeč zná tento základní rys Linqu.
Jinou obdobou téhož chování může být tento příklad - ten předpokládá, že se uchazečem pobavíme i o návratových hodnotách a lehce se tak dotkneme i návrhu rozhraní. Uchazeči je předložen takovýto kód:
internal class Program
{
private static void Main(string[] args)
{
INumberSource numberSource = new NumberSource();
var numbers = numberSource.GetNumbers();
var list = new List<int>(numbers);
if (list.Sum() != numbers.Sum())
{
throw new Exception("Something went wrong");
}
}
}
internal interface INumberSource
{
IEnumerable<int> GetNumbers();
}
internal class NumberSource : INumberSource
{
public IEnumerable<int> GetNumbers()
{
return Enumerable.Empty<int>();
}
}
První otázkou bývá, zda je kód v pořádku a zda nemůže dojít k vyvolání výjimky. V předloženém kódu ne, ale je vhodné používat IEnumerable jako návratový typ? A pokud ano, jak se s tím v kódu následně vypořádat (přiznám se, že IEnumerable jako návratový typ také používám). A co se stane, pokud se metoda změní takto:
public IEnumerable<int> GetNumbers()
{
Random rnd = new Random();
return Enumerable.Range(1, 10).Select(i => rnd.Next(0, 10));
}
S největší pravděpodobností dojde k vyvolání výjimky. A jak tomu zabránit? Jednou z možností je nepoužívat IEnumerable jako návratový typ, takže při přepsání na pole už bude vše v pořádku:
internal interface INumberSource
{
int[] GetNumbers();
}
internal class NumberSource : INumberSource
{
public int[] GetNumbers()
{
Random rnd = new Random();
return Enumerable.Range(1, 10).Select(i => rnd.Next(0, 10)).ToArray();
}
}
Ne vždy je to ale vhodné a chceme pracovat s pevnou kolekcí - takový přístup, tedy mít "pevnou" kolekci je více než vhodný při předávání výsledku mezi vrstvami aplikace (sám jsem kdysi řešil problém vzniklý předáním IEnumerable jako výsledku akce Controlleru v ASP.NET MVC :-) ), ale někdy se nám zase hodit nemusí a chceme mít skutečně návratový typ IEnumerable. V tom případě je odpovědnost na volajícím, který musí s podobným chováním počítat a vypořádat se s ním, třeba tím, že si "pevnou" kolekci hned vytvoří, například voláním ToArray():
private static void Main(string[] args)
{
INumberSource numberSource = new NumberSource();
var numbers = numberSource.GetNumbers().ToArray();
var list = new List<int>(numbers);
if (list.Sum() != numbers.Sum())
{
throw new Exception("Something went wrong, ");
}
}
Jak je tedy vidět, Linq může programátora někdy docela potrápit a být tak i vhodným tématem u pracovního pohovoru.
Žádné komentáře:
Okomentovat