C# alapok II
foreach¶
Java-ból már ismerős lehet a for ciklus for-each szerű használata, amikor nem az indexeken, hanem magukon az értékeken iterálunk végig.
1 2 3 4 5 6 |
|
Java-ban ehhez nem tartozik külön kulcsszó, viszont a C# nyelvben ez a foreach
segítségével tehető meg.
1 2 3 |
|
yield¶
A yield
kifejezés segítségével egy ciklusból olyan osztályt generál a fordító, ami megvalósítja az IEnumarable
interfészt.
Egy adott metódusban használjuk a yield
-et, majd ezt a metódust egy ciklusban többször meghívjuk.
Ilyen esetekben a yield
kétféleképpen működhet:
yield return <<expression>>;
: megőrződik a metódus állapota, és a következő metódushívásnál innen folytatódik tovább a működés,yield break;
:return
-nel egyenértékű lépést tesz, végleg kilép a metódusból és törlődik az állapot.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
A fenti kódrészlet a yield
segítségével a következőképpen egyszerűsödik:
1 2 3 4 5 6 7 8 9 10 11 |
|
A példa működése: első meghíváskor visszatér a 2-vel, megjegyzi az i index értékét, majd kilép.
A következő meghíváskor az i -- megjegyzett -- értékével folytatja a futást, így a 4-es értékkel tér vissza, és így tovább.
Ez a folyamat a yield break
segítségével szakítható meg.
Feladat: Próbáld ki a fenti példát! Helyezz egy breakpointot a metódusokba, és ellenőrizd a működésüket!
Típusellenőrzés és konverzió¶
Az is
és az as
kulcsszavak típuslekérésre illetve típuskonverzióra használhatók.
Az is
segítségével ellenőrizhető egy változó típusa:
1 2 |
|
Az as
használatával típuskonverzió hajtható végre:
1 2 |
|
sealed¶
A sealed
módosító segítségével osztályok, metódusok, properyk tehetők lezárttá.
Sealed osztályból gyerekosztály nem származtatható.
1 2 3 4 5 6 |
|
A metódusok és propertyk esetén pedig az öröklődés alatti felüldefiniálást akadályozza meg. A struktúrák C#-ban alapértelmezetten lezártan viselkednek, nem származtathatók.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
A sealed
kulcsszó csak az override
-dal együtt használható, illetve az abstract
módosítószóval nem használható, mivel akkor elvárás, hogy az adott metódus a gyermekosztályban implementálva legyen.
Kivételkezelés¶
Kivételkezelés a már megszokott try-catch-finally
segítségével valósítható meg.
A finally
nyitja a cleanup blokkot, ami minden esetben le fog futni, ezt egy vagy több catch
blokk előzheti meg, de nem kötelező a catch
blokk használata sem, ha van finally
.
A finally
blokk opcionálisan elhagyható, ha nincs szükségünk objektumok kézzel történő felszabadítására.
A finally
blokkra nem menedzselt kód esetén van szükség - ez az amit a GC nem takarít el helyettünk -, pl. ahogy már Javaban is találkozhattunk vele, pl. adatbázis műveletek során:
1 2 3 4 5 6 7 8 9 10 11 |
|
Erre már Java-ban is volt egy kényelmi megoldás: a try-with-resource
volt.
Itt az az egy megkötés volt, hogy a nem menedzselt objektumnak a java.lang.AutoClosable interfészből kell származnia, így a keretrendszer automatikusan le tudta zárni.
A try-with-resource
a try
blokk elhagyása után automatikusan meghívta az objektum -- jelen esetben adatbáziskapcsolat -- close
metódusát:
1 2 3 4 5 |
|
Hasonló megoldást tartogat a C# is, ez is kényelmi funkció, mint a Java-s try-with-resource
, a using
kulcsszó.
Fontos: ez a using
és az a using
, amivel namespace-eket használunk nem ugyanaz!
A using
esetén az objektumnak az IDisposable
interfészt kell megvalósítania, és a using
biztosítani fogja, hogy az objektum Dispose()
metódusa megfelelően legyen meghívva - akkor is ha kivétel keletkezik.
Ugyanazt a logikát követi, mint a try-with-resource
, itt a Dispose()
metódus fog felelni az erőforrás felszabadításáról, bezárásáról.
A Java és a C# kényelmi megoldásából is egy try-finally
fog generálódni a háttérben.
1 2 3 |
|
Delegatek¶
A delegate
felfogható függvénymutatóként, mely olyan függvényekre tud referálni, amelyeknek megegyezik a deklarációja -- paraméterlistája és a visszatérési értéke -- a delegate
objektuméval.
Egy delegate
létrehozása után bármilyen kompatibilis függvényhez hozzárendelhető, így függvény hívható a delegate
objektumon keresztül. Statikus és példánymetódusok is használhatók delegateken keresztül.
Általános használatuk: függvények átadása más függvényeknek argumentumként, így callback
függvények hozhatók létre.
1 |
|
A Del
delegate olyan függvényekre tud mutatni, melyek void
visszatéréssel rendelkeznek és egy string
paramétert várnak, mint pl:
1 2 3 |
|
1 2 |
|
Mivel a létrehozott delegateHandler
objektum, így átadható függvényeknek, mint paraméter.
1 2 3 4 5 |
|
Egy delegate objektum több metódusra is hivatkozhat egyszerre.
Amikor a delegate objektum meghívódik, az összes metódust meghívja feliratkozási sorrendben.
Egy delegatehez hozzáadható egy függvény a +=
segítségével és eltávolítható belőle a -=
segítségével.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Ez az eseménykezelés során hasznos, mikor egy eseményre feliratkozhatunk, illetve leiratkozhatunk saját eseménykezelővel a +=
és a -=
segítségével.
Func¶
A Func
egy speciális delegate, melynek lehetnek input paraméterei (akár nulla, de több is) és egy visszatérési értéke van.
1 2 3 4 5 6 |
|
Az utolsó paraméter a < > között a visszatérési érték, az előtte lévők az input paraméterek.
Ha egy függvény nem vár paramétereket és pl. double
-lel tér vissza, akkor a
1 |
|
Action¶
Hasonló az előző szekcióhoz, viszont az Action
-nak pontosan egy bemenő paramétere van és nincs visszatérési értéke.
1 2 3 4 5 6 7 |
|
A delegatek utolsó nagy csoportja az események, Events
.
Az események szemléltetése és megértése a WinForms környezetben egyszerűbb, mint konzolos alkalmazásokkal, így az ott kerül bemutatásra.
Feladatok delegate-ekkel¶
- Egészítsd ki a számológép alkalmazást, delegate-ekkel, minden műveletet delegate-ek segítségével végezz.
- Egészítsd ki a számológép alkalmazást, minden műveletet, illetve esetleges hibát logolj delegate segítségével.
Kollekciók¶
Több azonos típusú objektum együttes kezeléséhez eddig a tömbök nyújtották a megoldást. Amennyiben az objektumok száma előre ismert, akkor a tömb egy tökéletesen működő adatszerkezet, viszont ez a számosság nem mindig ismert előre. Ilyen esetekben az általános kollekciók rugalmasabb megoldást nyújtanak, méretben növekedni és zsugorodni tudnak attól függően, hogy hány objektum van tárolva.
1 2 3 4 5 6 7 8 9 10 11 |
|
A példában egy generikus List
objektum van létrehozva, amely string
típusú értékeket tud tárolni.
A konstruktorában egy számot vár, amely az iniciális kapacitást jelenti, alapértelmezett a 0.
Az Add
metódussal lehet hozzáadni elemeket a listához, illetve foreach
segítségével végig lehet rajta iterálni.
A példában látott Capacity
a tárolható objektumok számát adja vissza (azaz, hogy mennyi objektumnak foglalt már helyet a keretrendszer), míg a Count
azt, hogy ténylegesen hány objektum található a kollekcióban.
Ha foreach
helyett for
ciklus iterál végig a kollekción, akkor a Count
tulajdonságot kell használni.
A fenti példával egyenértékű írásmód érhető el az úgynevezett inicializáló listával:
1 2 3 4 5 |
|
Törölni a Remove(obj)
, illetve a RemoveAt(index)
metódusokkal lehet.
Az első egy objektumot vár, a második a kollekcióban egy indexet.
Kollekció típusok¶
A List
-en kívül más típusú kollekciók is elérhetők, mind más megvalósítással és működéssel, melyeket a következő táblázat mutatja be:
Osztály | Leírás |
---|---|
Dictionary <TKey, TValue> | kulcs-érték párokat tárol |
List <T> | objektumok listáját tárolja, támogatja az indexelést, keresést, rendezést és a lista módosítását |
Queue <T> | FIFO lista |
Stack <T> | LIFO lista |
A különböző kollekciók különböző metódusokat használnak az elemek kezelésére, a fejlesztőkörnyezet mindig a megfelelőt ajánlja fel használatukkor.
LINQ¶
A Language-Integrated Query, vagyis a LINQ lehetővé teszi a kollekciókban (bármiben, ami az IEnumerable
interfészt implementálja) történő keresést, rendezést és csoportosítást.
1 2 3 4 5 6 7 8 9 10 |
|
A kifejezés nem értékelődik ki addig, amíg a foreach (vagy egy más konstrukció) nem iterál végig a scoreQuery
-n.
1 2 3 4 5 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
Feladatok LINQ-el I¶
Készíts egy listát ami Pokémon objektumokat tárol. Egy Pokémon a következő tulajdonságokkal kell rendelkezzen: név, fajta, erő (szám). Ld. Pokédex, Complete Pokémon Pokédex.
- Listázd ki LINQ segítségével az összes sárkányt.
- Számold meg őket LINQ segítségével (1.).
- Keresd meg a legerősebbet LINQ segítségével. Az eredményben csak a neve és az ereje látszódjon. (1.)
- Listázd ki a 600-nál nagyobb erővel rendelkező összes Pokémont, csak a nevük szerepeljen az eredményben.
- Listázd ki őket (4.) abc sorrendben.
- Átlagold az összes Pokémon erejét.
Lambda kifejezések¶
A lambda kifejezéseknek két formája lehet:
1 2 |
|
Hasonló szintaktikával rendelkeznek, mint a függvények, viszont tömörebb írásmóddal érik el ugyanazt.
Minden lambda kifejezés delegate
-té alakítható.
1 2 3 4 5 |
|
1 2 3 4 5 |
|
1 2 3 4 5 |
|
LINQ kifejezések egy részét metódushívásos szintaktikával is lehet hívni, ilyenkor alkalmazhatók lambda kifejezések.
1 2 3 4 5 6 7 8 9 10 11 |
|
Hasznos metódusok:
Where
: szűrési feltételek megadhatók, pl.x => x > 80
,OrderBy
: rendezés az objektum (vagy valamely tulajdonsága szerint) pl..OrderBy(x => x.Age)
,ThenBy
:OrderBy
rendezés után egy újabb rendezés végrehajtása az előző megtartásával,Select
: kiválasztás, mi legyen az eredmény kollekcióban pl. a számok négyzete, anonym típus, stb pl..Select(x => (x.Age, x.Name))
,Any
: a kollekcióban van-e a feltételt kielégítő elem,All
: a kollekció minden eleme kielégíti-e a feltételt,Contains
: a kollekció tartalmaz-e egy specifikus elemet plnumbers.Contains(60)
,Average
: átlagszámítás,Count
: elemek számosságát határozza meg,Min
: minimum érték keresése,Max
: maximum érték keresése,Sum
: a kollekcióban fellelhető elemek összege,First
: a feltételeknek eleget tévő objektumok közül az elsőt adja vissza (kivételt dob ha nincs egy sem),FirstOrDefault
: mint az előző, csak kivétel helyett alapértelmezett értéket szolgáltat.
Feladatok LINQ-el II¶
A Pokémonos feladat összes pontját oldd meg metódushívásos formában is.
Kifejezésfák¶
LINQ használata esetén olyan tárolókon dolgozunk, amelyek vagy az IEnumerable vagy az IQueryable interfészt valósítják meg. Általános esetben az IEnumerable típusokon készített query-k delegate-ekké fordulnak, az IQueryable típusokon alapulók pedig expression tree-vé, azaz kifejezésfákká.
A kifejezésfák olyan formában reprezentálják a kódot, ami lehetőséget ad a futásidejű manipulációra. Ugyanazokat a struktúrákat használja, amit a fordító használ a kód analizálására és az lefordított output generálására. A segítségével módosíthatunk épp futó algoritmusokat stb., tehát láthatóan kiszélesíti a lehetőségeinket.
Természetesen mi magunk is előállíthatunk ilyen kifejezésfákat (ehhez a System.Linq.Expression névtér használatára lesz szükségünk), ha speciálisan velük szeretnénk dolgozni adott szituációkban. Ez egy olyan kódot jelent, ami nincs lefordítva és nem futtatható. Bizonyos típusokat (a LambdaExpression típusúakat, vagy az ebből származókat) le tudjuk fordítani egy köztes nyelvre, hogy direktbe futtatóvá tehessük (IL, intermediate language). Expression tree-k írásával dinamikusan készítünk lambdákat, és nem a végeredményre tesszük a hangsúlyt, hanem arra, hogy milyen lépésekkel jutunk el oda. Az ereje pedig nem abban áll, hogy C# kódban felhasználható, hanem hogy más rendszerekbe beintegrálható.
Most példaképpen egy ilyen LambdaExpression típussal fogunk megismerkedni:
1 2 3 4 |
|
Tehát ő egy expression tree-t fog előállítani, ami visszatér egy delegate-tel, és ezen a delegate-n keresztül tudjuk meghívni és futtatni.
Hivatkozások¶
Type-testing operators and cast expression
Language Integrated Query (LINQ)
Query Syntax and Method Syntax in LINQ
Framework Types Supporting Expression Trees
C# Introduction to Expression Trees
Extra¶
Really silly things to do with C# expression trees