pondělí 11. dubna 2016

Linq a pracovní pohovor


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