Skip Navigation LinksMS Academic > Blogovi > Detaljno

C# funkcionalno programiranje - LINQ biblioteka

Language INtegrated Queries (LINQ) je proširenje C# jezika koje vam znatno olakšava programiranje. Ovde možete naći neke korisne LINQ primere.

<< Generički delegati

Uvod

Dok programirate, veoma često nailazite na slične probleme koje treba da rešite kao što su:

  • Pronaći objekat po identifikatoru
  • Pronaći sve objekte u kolekciji koji zadovoljavaju neki uslov
  • Odrediti broj objekata u kolekciji koji zadovoljavaju neki uslov

Ovo su standardne funkcionalnosti koje se jednostavno koriste u upitnim jezicima kao što je SQL, ali u većini programskih jezika morate da napravite neku petlju kojom prolazite kroz kolekciju i proveravate za svaki objekat da li zadovoljava uslov. Ovo je standardan način imperativnog programiranja u kome morate eksplicitno da isprogramirate algoritam kojim će se rešiti ovi problemi.

Alternativa ovakvom načinu rada je takozvano funkcionalno programiranje u kome više deklerativno definišete šta želite da se uradi i iskoristite neku biblioteku koja će te algoritme uraditi za vas. Možete videti više detalja o osnovnim konceptima funkcionalnog progrmiranja u "C# funkcionalno programiranje - uvod". Srećom u C# podržava ovakvo funkcionalno programiranje pomoću LINQ biblioteke. LINQ je ugrađeni mehanizam kojim ovakve operacije možete da uradite u par linija koda.

LINQ

Language INtegrated Queries (LINQ) je proširenje C# jezika kojim se u C# uvode funkcionalnosti slične SQL upitima. Ukratko LINQ je skup C# funkcija koje će vam olakšati život dok programirate. LINQ funkcije se mogu prikazati u sledećem obliku:

<<kolekcija>>.<<funkcija>>( <<uslov>> )

U ovom primeru <<kolekcija>> je neki niz, enumeracija, lista ili bilo kakva kolekcija objekata, <<funkcija>> je neka funkcija iz LINQ biblioteke, a <<uslov>> je funkcija koja će biti primenjena nad svakim objektom u kolekciji, i u većini slučajeva govori da li LINQ funkciju treba primeniti nad objektom. <<uslov>> je funkcija (delegat ili lamba izraz) koja kao argument prihvata trenutni objekat u kolekciji a kao rezultat vraća logićki izraz koji govori dali je uslov zadovoljen ili ne (naravno postoje izuzetci). Primeri uslova su:

delegate(Knjiga knjiga) { return knjiga.Cena < 500; }
knjiga => knjiga.Cena < 500

U prvom primeru uslov je delegat koji prihvata argument knjiga i vraća kao rezultat da li je knjiga jeftinija od 500 dinara. Drugi primer je ekvivalentan lambda izraz u kome se prihvata argument knjiga i vraća da li joj je cena manja od 500. Oba izraza su ekvivalentna samo je lambda izraz malo kompaktnija sintaksa - argument je odvojen od tela metode znakom =>. Primeri dve LINQ funkcije koje koriste ove usolve da nađu jeftine knjige iz kolekcije knjiga su:

var jeftine = knjige.Where( delegate(Knjiga knjiga){ return knjiga.Cena < 500; } );
var jeftine = knjige.Where( knjiga => knjiga.Cena < 500 );

Zbog kompaktnosti sintakse ovde ću koristiti lambda izraze. Ovo će biti jasnije kroz primere koji slede.

Pored ovog oblika postoji ekvivalnetni oblik u vidu upita. Međutim ovde će biti predstavljene samo LINQ funkcije.

Pronalaženje elementa u nizu

Jedan od najčešćih problema koje morate da rešite je pronalaženje objekta u nizu po identifikatoru. Na primer, pretpostavimo da iz niza knjiga treba pronaći knjigu sa identifikatorom 17. Standardno ćete morati da napravite petlju kroz kolekciju knjiga i da za svaku knjigu proveravate da li joj je identifikator 17 ako jeste to je knjiga koju tražite.

var knjiga = knjige.First( k=> k.Id = 17 )
knjiga = knjige.FirstOrDefault( k=> k.Id = 17 )

Prvi poziv će u kolekciji knjiga pronaci prvu za koju vazi da joj je identifikator 17 kao što je definisano u lambda izrazu koji će biti izvršen nad svakom knjigom u kolekciji. U slučaju da ne nađe odgovarajuću knjigu ovaj poziv će baciti izuzetak.

Druga funkcija FirstOrDefault radi slično, s tim što ne baca izuzetak ako ne nađe objekat nego vrati podrazumevanu vrednost za taj tip (recimo null).

U slučaju da želite da vratite podskup objekata koji zadovoljavaju neki uslov možete koristiti Where funkciju umesto First kao što je prikazano u sledećem primeru:

var jeftine = knjige.Where( knjiga => knjiga.Cena < 500 );

U ovom primeru su pronađene sve knjige koje zadovoljavaju uslov u lambda izrazu to jest da je cena manja od 500.

Agregacije

Često morate da odredite maksimalni/minimalni element u kolekciji, saberete sve elemente, odredite srednju vrednost i slično. Veoma često ovo morate da uradite samo nad nekim elementima kolekcije a ne nad celom kolekcijom. Standardni pristup je da napravite petlju kroz kolekciju, proverite za svaki da li zadovoljava uslov i ako zadovoljava, ažurirate brojač, sumu, srednju vrednost i slično. Međutim, LINQ vam pruža gotove funkcije za minimum, maksimum, sume srednje vrednosti i slično. Ono što je bitno je da ove funkcije možete primeniti i na delovima kolekcije tako što im prosledite neki uslov. Na primer, ako pretpostavimo da su brojevi niz brojeva a knjige kolekcija knjiga, neki primeri korišćenja LINQ funkcija su:

brojevi.Average( broj => broj < 200 );
brojevi.Min( broj => broj%2 == 1 );
knjige.Count( knjiga => knjiga.Sekcija == "C#" );

U prva dva primera se pronalaze srednja vrednost svih vrojeva manjih od 200 i najmanju neparan broj u kolekciji. U trećem brimeru se pronalazi broj knjia u sekciji C#. Kao što možete videti jedna linija vam zamenjuje petlju u kojoj biste morali da proveravate uslove i procesirate rezultate.

Provere uslova

Veoma često morate da proverite da li bar jedan ili svi elementi u kolekciji zadovoljavaju neki uslov. LINQ vam omogućava da pomoću ugrađenih funkcija zamenite petlje sa proverama sa jednom linijom koda. Neki primeri su:

if( knjige.Any( knjiga => knjiga.Cena < 200) ) {
}
if( brojevi.All( broj => broj%2 == 0) ) {
}

U prvom primeru se proverava da li postoji knjiga jeftinija od 200 dinara, u drugom se proverava da li su svi brojevi u nizu parni. Kao što možete videti ovo je jednostavna zamena za standardne petlje a i kod je čitljiviji.

Rad sa kolekcijama

LINQ vam omogućava da lako prolazite kroz kolekciju i da preskačete ili uzimate sekvence elemenata u zavisnosti od nekog uslova. 

brojevi.SkipWhile( b => b < 10 );
brojevi.TakeWhile( b => b < 10 );
brojevi.SkipWhile( b => b < 10 ).TakeWhile( b => b < 10 );

U prvom primeru u nizu brojeva su preskočeni svi jednocifreni brojevi sa početka niza. U drugom primeru su u novu kolekciju ubačeni svi uzastopni dvocifreni brojevi sa početka niza. U trećem primeru možete videti kombinaciju ove dve metode gde su u kolekciji prvo preskočeni svi uzasztopni brojevi sa početka niza, a onda izvučeni svi uzastopni dvocifreni brojevi koji ih slede.

Ove funkcije možete efikasno koristiti radi ekstrakcije reči u tekstu, gde preskačete nizove uzastopnih karaktera koji nisu slova, a uzimate uzastopne sekvence karaktera koji predstavljaju pojedine reči. Pored uslova, postoje i oblici u kojima se umesto uslova prosledi samo broj elemenata koji bi trebalo da budu preskočeni ili uzeti iz kolekcije - primer je prikazan u sledećem kodu:

brojevi.Skip( 30 ).Take( 10 );

Ovaj oblik je veoma pogodan ako vam je potrebno neko straničenje(paginacija), koje lako možete da implementirate pomoću LINQ-a.

Manipulacija kolekcijama

Pored mogućnosti da čitate elemente iz kolekcija, LINQ vam omogućava da lako manipulišete kolekcijama u smilsu da možete da ih sortirate, spajate i slično. Primer u kome je prikazano kako možete da sortirate elemente u kolekciji je prikazan u sledećem kodu:

knjige.OrderBy(k=>k.Cena).ThenBy( k=>k.Naslov ).Distinct().Reverse();

Primetite da u ovom slučaju lambda izraz koji je prosleđen ne vraća vrednot tačno/netačno tj. nije predikat. U ovom primeru, funkcija prosleđena LINQ funkciji govori na koji način treba izvući podatke iz svakog elementa i ta informacije će biti korišćena prilikom sortiranja. Konkretno prilikom prvog sortiranja, LINQ će primeniti na svaku knjigu lambda izraz koji na osnovu knjige vraća cenu. U drugom sortiranju će koristiti lambda izraz koji na osnovu knjige vraća vrednost naslova. Na kraju su primenjene metode Distinct i Reverse koje izvlače samo različite elemente niza a onda im obrnu redosled.

Pored toga, na raspolaganju vam je veliki broj funkcija za rad sa kolekcijama kao što su Union, Except, Concat, Intersect i tako dalje.

Konvertovanje kolekcija

LINQ ima nekoliko korisnih funkcija kojima možete da jednu kolekciju konvertujete u drugu. Na primer možete koristiti metode ToArray, ToList da konvertujete kolekciju u niz ili listu. Pored toga, zanimljiva je i funkcija koja kreira heš tabelu na osnovu kolekcije:

Dictionary<string, Knjiga> knjigePoISBN = knjige.ToDictionary(  knjiga => knjiga .ISBN);
Dictionary<string, double> cenePoISBN = knjige.ToDictionary(    knjiga => knjiga.ISBN,
                                                                 knjiga=>knjiga.Cena);

U prvom primeru je na osnovu kolekcije knjiga napravljena heš tabela gde je ključ ISBN knjige a vrednost sama knjiga. U drugom primeru je napravljena heš tabela u kojoj se na osnovu ISBN ključa nalazi cena knjige. U ovom primeru prvi lambda izraz za svaku knjigu vraća vrednost ključa, dok drugi lambda izraz određuje vrednost koja će biti uneta u heš tabelu po ključu.

Pored toga možete da koristite metodu Select da odaberete neka polja iz kolekcije kojima se kreira nova kolekcija. Primer korišćenja Select metode je dat u sledećem primeru:

IEnumerable<string> naslovi = knjige.Select(knjiga=>knjiga.Title);
IEnumerable<Book> books = knjige.Select( knjiga => new Book(knjiga.ISBN, 
                                                            knjiga.Naslov,
                                                            knjiga.Cena*107);
var kolekcija = knjige.Select( knjiga => new { ISBN = knjiga.ISBN, 
                                               Title = knjiga.Naslov,
                                               Price = knjiga.Cena*107}
                                        );

Prvi primer na osnovu kolekcije knjiga pravi novu kolekciju koja sadrži samo string informacije o naslovima - ovde smo iz objekta knjiga odabrali samo naslov. U drugom primeru smo na osnovu kolekcije knjiga napravili novu kolekciju klasa tipa Book. Pretpostavka je da klasa Book ima samo ISBN, naslov i cenu u evrima tako da su ovi parametri predati konstruktoru. Pri tome je cena u dinarima konvertovana u evre. Treći primer je sličan drugom uz razliku da se umesto postojeće klase Book koristi dinamički kreirana anonimna klasa koja ima tri polja.

Zaključak

LINQ funkcije će vam olakšati rad sa kolekcijama pošto većinu operacija možete da izvršite sa par postojećih funkcija koje su vam na raspolaganju. Kao što možete videti korišćenje LINQ funkcija je jednostavno i zbog toga, ako radite na C# preporučujem vam da ih koristite gde god možete. Ako vas zanima šta još može da se uradi pomoću LINQ proširenja preporučujem vam da vidite ove artikle:

  1. http://code.msdn.microsoft.com/101-LINQ-Samples-3fb9811b Primeri LINQ koda koje možete preuzeti
  2. http://www.codeproject.com/Articles/286255/Using-LINQ-Queries tutorijal o LINQ funkcijama i upitima.

Sledeći post: Asinhroni delegati>>

Komentari

Nema komentara na temu.

Pošaljite komentar

Komentari će biti prikazani pošto ih odobri moderator sajta. Da bi komentari bili objavljeni, moraju biti u skladu sa politikom objavljivanja.




Napomena: Usled velikog broja automatski generisanih komentara (spam) prinuđeni smo da uvedemo dodatnu verifikaciju. Molimo vas da pre slanja vašeg komentara unesete u polje rezultat zbira.

captcha

Upišite rezultat: