Bevezető A probléma aszinkronitás... 1 Mi az az Observable?... 1 Mi az a LINQ?... 3 LINQ vs Rx Hello Rx... 9

Méret: px
Mutatás kezdődik a ... oldaltól:

Download "Bevezető... 1. A probléma aszinkronitás... 1 Mi az az Observable?... 1 Mi az a LINQ?... 3 LINQ vs Rx... 8. Hello Rx... 9"

Átírás

1

2 Tartalomjegyzék Bevezető... 1 A probléma aszinkronitás... 1 Mi az az Observable?... 1 Mi az a LINQ?... 3 LINQ vs Rx... 8 Hello Rx... 9 Előkészületek... 9 Hagyományos megközelítés Rx megközelítés Összefoglalás Rx = Adatfolyamok + LINQ + Ütemezők Előkészületek Adatfolyamok LINQ Ütemezők Rx + Async Összefoglalás... 82

3 Bevezető A probléma aszinkronitás Modern alkalmazások fejlesztésénél (legyen az vékony- vagy vastagkliens, vagy kiszolgáló oldali szoftver) egy bizonyos ponton mindenképpen elérjük a szinkron módon futó kód korlátait ekkor kerülünk szembe az aszinkron és eseményvezérelt programozás nehézségeivel. Ha az alkalmazás megfelelő minőségének megőrzése érdekében az I/O műveleteket, webszolgáltatás-hívásokat, vagy épp egyéb erőforrás igényes műveleteket aszinkron módon kezeljük, a kód lényegesen bonyolultabbá válik. A megszokottól eltérő módszereket kell használnunk a koordinációra, a kivételkezelésre, és felvetődik a megszakíthatóság, valamint a párhuzamosan futó aszinkron feladatok szinkronizációjának problémája is. A Reactive Extensions (a továbbiakban Rx) egy olyan osztálykönyvtár, melynek segítségével aszinkron és/vagy eseményvezérelt programot készíthetünk úgy, hogy az adatfolyamot úgynevezett megfigyelhető (Observable) szekvenciaként reprezentáljuk, melyen LINQ-jellegű műveleteket hajthatunk végre úgy, hogy az esetleges versenyhelyzeteket úgynevezett ütemezőkkel (Scheduler) oldjuk fel. Röviden: Rx = Observables + LINQ + Schedulers Mi az az Observable? Ahhoz, hogy eljussunk az Observable fogalmáig, meg kell ismerkednünk az előtte lévő két lépcsőfokkal: a szinkron és az aszinkron programozás fogalmával. Az esetek túlnyomó részében a kódunk lényegi része úgynevezett szinkron kód. Azaz olyan utasítások, melyek sorban egymást követve hajtódnak végre. Kiadunk egy utasítást, elvégzi a processzor, majd mikor végzett (ez a fontos része a dolognak) továbblép a következő utasításra, és így tovább. A hagyományos konzol alkalmazások tipikusan szinkron utasításokat tartalmaztak olyannyira, hogy ha épp olyan pontjához értünk az alkalmazásnak, hogy a felhasználótól vártunk valamilyen adatbevitelre, akkor amíg a felhasználó nem írt be valamit, addig az egész program állt egy helyben. Ezzel ellentétben az aszinkronitás alapvetően azt jelenti, hogy valami olyan esemény hatására akarunk műveletet végezni, amiről nem tudjuk, hogy pontosan mikor fog bekövetkezni. 1

4 Ilyen például egy felhasználói felületre kihelyezett gomb Click eseménye, de ilyen például egy webszolgáltatás-hívás visszatérési értéke is. Ez utóbbi esetben ugyan elképzelhető, hogy az utasítások sorrendje szempontjából pontosan tudjuk, hogy az után akarunk bármit is csinálni, miután a szervizhívásnak visszakaptuk az eredményét, azonban itt számolni kell azzal, hogy ez akár másodpercekbe is kerülhet és ha ez szinkron módon lenne megírva, akkor annak a szálnak a végrehajtása ami elindította a szervizhívást addig nem lépne tovább, amíg a szervizhívás vissza nem tér, azaz az a szál blokkolva lenne. Ez pedig különösen kellemetlenül tudja érinteni a felhasználót, ha a UI szálon történik, mert akkor bizony amíg a hívás nem tér vissza, addig az alkalmazás megfagy, válaszképtelenné válik. Tehát annak érdekében, hogy ez ne történjen meg, aszinkron hívást fogunk használni, mely azt jelenti, hogy elindítjuk a hívást, a hívás lényegi része egy külön szálon fut (vagy vár), majd mikor megvan az eredmény értesít egy eseményen keresztül. Ilyenkor a hívás elindítása után rögtön folytatódik az eredeti (hívó) szálon az utasítások végrehajtása, az eredménnyel dolgozó kódunk pedig belekerül a CallBack metódusba vagy Completed eseménykezelőbe. Szerencsére a Microsoft ez utóbbi problémát lényegesen leegyszerűsítette a C# 5.0-ban azzal, hogy bevezette az async és await kulcsszavakat, melyek segítségével úgy tudunk aszinkron hívásokat írni, mintha csak szinkron hívások lennének. Ugyan rengeteg (egyszerű) szituációban ez megkönnyíti az életünket, de amint szeretnénk egy AutoRetry, Timeout, vagy épp Paging funkcionalitást adni egy webszolgáltatás-híváshoz, rögtön állhatunk neki kódolni, mert ez ellen már nem véd meg az, ha elrejtik előlünk a CallBack metódusok megírását. Az eseményeknek azonban van egy komoly problémája, mégpedig, hogy nem megfoghatóak és egy adott eseményre feliratkozott figyelők nyomonkövetése sem triviális. Ezen segít az Observer design pattern azzal, hogy lényegében explicit módon leimplementálja az eseménykezelést. Ezzel azonban sokkal nagyobb kontrollt kapunk a folyamatban, és ami még fontosabb (később meglátjuk miért), megfoghatóvá teszi az eseményeket. Ez a programozási minta alapvetően két interfészből áll. Az egyik az IObservable<T>, mely egy megfigyelhető adatforrást reprezentál. Ez az interfész mindösszesen egy Subscribe() metódust definiál, ami lényegében az eseményes világ += operátorának felel meg. A másik az IObserver<T>, mely egy megfigyelőt reprezentál, amit átadhatunk egy adatforrásra való feliratkozáskor. Ennek az interfésznek az eredeti programozási minta szerint van egy Notify() metódusa, ezen keresztül értesíti az adatforrás a rá feliratkoztatott megfigyelőket. Rx esetében nem a szabvány implementációval kell dolgoznunk, hanem annak egy speciális változatával. Itt az IObserver<T> interfész három metódust definiál, melyek az OnNext(), OnError() és az OnCompleted(). A legelső kerül meghívásra, ha egy új elem jelenik meg az adatfolyamban, a második, ha valami probléma történne, és az utolsó egy lezáró üzenet, melynek hatására a megfigyelés befejeződik, ezzel jelzi az adatforrás, hogy nem fog több értéket szolgáltatni. 2

5 Az Rx erre a két interfészre épül és ezeknek a segítségével tudja lényegesen leegyszerűsíteni a legkülönfélébb típusú aszinkron adatfolyamok kezelését. Annak érdekében, hogy megértsük miért, meg kell ismerni legalább alap szinten a LINQ-t. Mi az a LINQ? A LINQ (Language Integrated Query) a.net 3.5 óta könnyíti meg a fejlesztők életét [7][8][9], mikor oda kerül a sor, hogy valamilyen adathalmazzal kell dolgozni. Ez a technológia, illetve talán még inkább az általa bevezetett nyelvi elemek ültetik el a C# (illetve Visual Basic) nyelvben a funkcionális programozás csíráit. Nem megyek bele messzemenő részletekig, hogy mit is takar a funkcionális programozás azonban idéznék egy nagyon jó metaforát, melyet Luca Bolognese mondott a 2008-as PDC-n az F#-ról szóló előadásában. "Funkcionális programozásnál a jelszó az, hogy "mit" és nem az, hogy "hogyan". Erre egy nagyon jó példa, hogy ha bemegyünk egy bárba és kérünk egy kávét, akkor a pultosnak nem fogjuk megmondani, hogy őrölje meg a kávét, forralja fel a vizet, készítse elő a cukrot és a tejet, és tegye mindezt párhuzamosan, hanem csak egyszerűen kérünk egy kávét." Hogy egy kicsit komolyabb példán keresztül szemléltessem a gondolatmenetet, gondoljuk el, hogy hogyan oldanánk meg egy olyan feladatot, amiben egy számsorozatból kell kiválasztanunk a páros számokat, majd össze kell adni a kiválasztott számok négyzeteit? Nagy valószínűséggel első körben az 1-1 kódrészlethez hasonló megoldásra jutnánk. 3

6 1-1 kódrészlet: Páros számok négyzetének összege első megközelítés List<int> számok = new List<int>(); számok.add(1); számok.add(2); számok.add(3); számok.add(4); számok.add(5); számok.add(6); számok.add(7); számok.add(8); számok.add(9); számok.add(10); int akkumulátor = 0; foreach (int szám in számok) if (szám % 2 == 0) akkumulátor += (szám * szám); } } return akkumulátor; Mit lehet ezzel a konkrét kóddal tenni? Hogy tudnánk kicsit tömörebbre, átláthatóbbra varázsolni? Kezdjük például a számok létrehozásánál: 1-2 kódrészlet: Object Initializer var számok = new List<int>() 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; Vegyük sorba milyen érdekességek vannak ebben a sorban. Kezdődik a var kulcsszóval. Mikor egy változót ezzel a kulcsszóval deklarálunk, akkor a fordító a deklaráció egyenlőségjele utáni kifejezésből deríti ki, hogy a változó milyen típusú. Jelen esetben, ha felé vinnénk az egeret, akkor mutatná is rögtön, hogy a számok változó List<int> típusú. Ez a nyelv egy kényelmi kiterjesztése, bár a későbbiekben találkozni fogunk egy olyan képességgel, mely a var kulcsszó nélkül nem volna megvalósítható. Ha továbbmegyünk, egy érdekes szintaktikával találkozunk a példányosításnál. A sor nem ér véget a konstruktorhívással, hanem kapcsos zárójelek között megadjuk a lista elemeit is. Ezt nevezzük Object Initializer szintaktikának. Ugyan a példához nem tartozik hozzá, de ha az előző kettő nyelvi elemet összekombináljuk, akkor megkapjuk az úgynevezett névtelen típusokat. Gondoljunk bele abba a szituációba, amikor egy ciklus kellős közepén arra volna szükségünk, hogy egy átmeneti változóba több különböző információt is elmentsünk. A LINQ előtt ilyenkor jött az, hogy mindegyik részinformációnak létrehoztunk egy 4

7 változót és azokat használtuk ilyen jellegű átmeneti információk tárolására. Ha azonban névtelen típust használunk, megtehetjük, hogy egy ilyen kódot írunk: var ember = new Név = név, Életkor = kor }; 1-3 kódrészlet: Névtelen típus Itt aztán nézheti a fordító a kifejezés jobb oldalát, nem fogja kitalálni abból sem a változó típusát. Ilyenkor valójában a színfalak mögött a fordító létrehoz egy típust az adott változónak, mely tartalmazza a megadott tulajdonságokat. Menjünk tovább az eredeti példa rövidítésén és tekintsük meg a következő lépést: 1-4 kódrészlet: Páros számok négyzetének összege LINQ megközelítés return számok.where(szám => szám % 2 == 0).Select(szám => szám * szám).sum(); Ha kicsit visszalaolvasunk, akkor láthatjuk, hogy a számok változó egy int típusú lista. Hogyan tudunk rajta Where() metódust hívni, mikor nincs is olyan metódusa a List<T> típusnak? Erre a kérdésre a választ az úgynevezett Extension Method -ok adják. Ha egy tetszőleges típushoz új funkcionalitást akarunk adni, akkor a triviális megoldás, hogy származtatunk belőle egy osztályt, majd abban definiáljuk az extra funkcionalitást. Ezzel azonban két probléma van. Egyrészről elképzelhető, hogy az eredeti típus sealed, azaz már eleve örököltetni sem tudunk belőle, de még ha ez a probléma nem is áll fenn, akkor is ott van az a kényelmetlen tény, hogy onnantól kezdve mindig a saját típusunkat kell használnunk, vagy konverziós metódusokat definiálnunk, mellyel műveletvégzés előtt az eredeti típusból a mi típusunkká alakítjuk az adatot, majd elvégezzük rajta a műveletet és végül a módosított adatot visszakonvertáljuk az eredeti típusba. Nem kell bonyolult dologra gondolni, elképzelhető, hogy van egy egyéni függvényünk string típusú adatok formázására. Amennyiben ezt a függvényt több helyen is fel akarjuk használni, akkor vagy kirakjuk egy statikus osztályba, vagy készítünk egy saját SuperString típust, mely tartalmazza ezt a függvényt. Ez utóbbit a konkrét példánál maradva pont nem tehetnénk meg, mert a String típus sealed, azaz nem lehet belőle örököltetni. Marad a statikus osztályos megoldás, de gondoljuk el, hogy nézne ki az a kód ahol esetleg megpróbálunk egymás után két ilyen függvényt is használni Ezen probléma megoldására vezette be a Microsoft az Extension Methodokat, mellyel tetszőleges osztályhoz (sőt akár interfészhez is) definiálhatunk függvényeket a következő szignatúrával: 5

8 1-5 kódrészlet: Extension Method szignatúrája public static string SpecialFormat(this string s, int spaces) Ezeket a függvényeket egy statikus osztályba kell beletennünk, és maguknak a függvényeknek is a szignatúrájuk szerint statikusnak kell lenniük. Ez után a varázslat az első paraméternél keresendő. A this kulcsszó után következő típus az, amit ki fogunk ezzel a metódussal terjeszteni. Ezt a metódust csak a kiterjesztett osztály egy példányán lehet meghívni, szóval a szignatúrájával ellentétben pont, hogy nem lesz statikus. Ezen példányra kapunk referenciát az s változó által. A metódus meghívásakor ez a paraméter nem fog látszódni a listában, csak az ez utáni paraméterek (esetünkben az spaces). Használata tehát a következőképpen fog kinézni: defaultstring.specialformat(42); 1-6 kódrészlet: Extension Method használata A LINQ egy olyan mintát használ az extension method-jainál, amivel ezt egy szinttel magasabbra helyezi. Hogy az eredeti példánknál maradjunk, nézzük meg, hogy néz ki a Where() metódus szignatúrája: 1-7 kódrészlet: LINQ Where() Extension Method szignatúrája public static IEnumerable<T> Where<T>(this IEnumerable<T> enumerable, Func<T, bool> predicate) Az egyik, amit érdemes meglátni, hogy az IEnumerable<T> interfészt terjeszti ki és a visszatérési értéke is ugyanilyen típusú. A LINQ szinte mindegyik operátorának szignatúrája így kezdődik, ez pedig azért jó, mert lehetővé teszi a kompozíciót, azaz, hogy az operátorokat tetszőleges hosszan egymás után fűzzük. Ez a jelenlegi példán is jól látszódik, bármiféle köztes változók bevezetése nélkül képesek voltunk egymás után fűzni a Where(), majd a Select() és végül a Sum() függvényeket. A másik jellemző dolog a LINQ operátorokkal kapcsolatban az, hogy a munka lényegi részét ránk bízzák. Ez azt jelenti, hogy például a Where() metódus tartalmazza azt a kódot, hogy végigmenjen a lista elemein, válassza ki valamilyen feltétel alapján, hogy melyik maradjon, melyik ne, és végül adja vissza az eredményül kapott szűrt listát, de egy részét nem tartalmazza a logikának, mégpedig a konkrét feltételvizsgálatot, hogy mi alapján menjen vagy maradjon egy elem. Pontosan ezen okból kifolyólag van a paraméterek között a Func<T, bool> delegált, mely egy olyan metódust reprezentál, ami megkapja a kollekció egy elemét paraméterül, majd csinál vele amit akar és visszaad egy bool értéket, hogy megfelelt, vagy nem felelt meg valamilyen feltételnek. 6

9 Ha tovább nézzük az eredeti példát, nem is kell sokat olvasnunk máris találunk egy igen érdekes kifejezést a Where() metódusnak (meg a Select()-nek is) paraméterül adva. szám => szám % 2 == kódrészlet: Lambda kifejezés Ezt nevezzük Lambda kifejezésnek, vagy névtelen metódusnak. Ezzel a kifejezéssel lehetőségünk van arra, hogy anélkül írjunk egy metódust, hogy írnánk egy metódust. Mint azt az imént említettem, nagyon sok olyan szituáció van, amikor egy metódus paraméterül vár egy delegáltat. Ilyenkor normál esetben azt csináljuk, hogy elkészítjük a szükséges metódust, majd átadjuk azt paraméterül a szóban forgó metódusnak. Ennél azonban lényegesen átláthatóbb lehetőséget nyújt a Lambda kifejezés, mellyel helyben tudjuk megadni a szükséges funkcionalitást. A Lambda kifejezések teljes szintaktikája a következőképpen néz ki: (param_1, param_2, param_n) =>... return valami; }; 1-9 kódrészlet: Lambda kifejezés teljes pompájában A nyíl operátor (=>) előtt zárójelben, vesszővel elválasztva a kifejezés paraméterei, majd a nyíl után kapcsos zárójelek között a kifejezés törzse helyezkedik el. A paramétereknek azért nem kell típust írnunk, mert azt a fordító kitalálja abból, hogy milyen delegáltnak adjuk át a kifejezést. Amennyiben csak egy paraméter van, úgy a zárójel elhagyható, ha viszont nincs paraméter, akkor ki kell írnunk egy üres nyitó-csukó zárójelet. A törzs esetében is van egy rövidítési lehetőségünk. Amennyiben a névtelen metódus mindössze egyetlen sorból áll, amely kifejezésnek az eredménye egyben a visszatérési értéke is a metódusnak, akkor nincs szükség se kapcsos zárójelre, se return kulcsszóra. És persze ne feledkezzünk meg arról sem, hogy ha esetleg mégis megvolna az eredeti delegált szignatúrájának megfelelő metódus, akkor elegendő mindössze a metódus nevét átadnunk, nem kell olyan kifejezést írni, hogy Metodus(x => F(x)), hanem elegendő csak annyit írni, hogy Metodus(F). Végül pedig csupán puszta érdekességként megjegyezném, hogy az aktuális példánk párhuzamosítása, mindössze egyetlen extra operátor beszúrásából áll: 7

10 számok.asparallel().where(szám => szám % 2 == 0).Select(szám => szám * szám).sum(); 1-10 kódrészlet: LINQ párhuzamosítás Az AsParallel() operátornak köszönhetően a teljes lekérdezés párhuzamosan fog futni. Gondoljunk csak bele, ha ezt az újonnan felröppenő igényt az eredeti, hagyományos módon elkészített kódunkban kellene megoldani... A LINQ tehát ezeket a technológiákat és rengeteg előre definiált Extension Methodot jelent több különböző típuson, többek közt például az IEnumerable<T>-n is, mely igazából ugye nem is osztály, hanem interfész, így azonban minden olyan osztály, ami megvalósítja ezt az interfészt, megkapja az extra metódusokat is. Ezen metódusok túlnyomó részének a visszatérési értéke is IEnumerable<T>, azaz kényelmesen egymás után láncolható rengeteg ilyen operátor, melyek egy forráskollekciót szép sorjában egyenként valamilyen logika alapján átalakítanak, átrendeznek vagy szűrnek, majd továbbadják az eredményt a következő operátornak. A LINQ, mint arra utaltam is, nem csak az IEnumerable<T> típushoz ad kiterjesztéseket, hanem egyéb adatforrásokhoz is, mint például relációs adatbázisok (LINQ to SQL) vagy XML dokumentumok (LINQ to XML) és még sok más. Amennyiben pedig az adatforrás történetesen aszinkron események sorozata, akkor eljutunk a LINQ to Events-hez, azaz a Reactive Extensions-höz. LINQ vs Rx A LINQ meglévő kollekciókon tud dolgozni (konkrétan IEnumerable<T> interfészt megvalósító típusokon) és mélyen az alapját az IEnumerable<T> és IEnumerator<T> interfészpáros adja. Vele ellentétben az Rx olyan adatfolyamokon dolgozik, melynek még nem áll rendelkezésre minden eleme és mélyen az alapját az IObservable<T> és IObserver<T> interfészpáros képzi. Míg a LINQ az úgynevezett polling technikával kérdezgeti az IEnumerator<T> objektum MoveNext() metódusán keresztül a listában soron következő elemet, addig az Rx esetében push technikát alkalmazva az IObserver<T> objektum OnNext() eseménye kerül meghívásra, ha egy új elem érkezik az adatfolyamba. Ugyanúgy, ahogy a LINQ valamilyen bemeneti kollekcióból különböző szelekciós, projekciós, csoportosító, összekapcsoló stb. operátorok használatával egy kimeneti kollekciót készít, az Rx valamilyen bemeneti adatfolyamot áttranszformálva egy kimeneti adatfolyamot készít. És végül, míg a LINQ az IEnumerable<T> interfészhez nyújt kiegészítést, addig az Rx az IObservable<T>-hez. 8

11 Hello Rx Előkészületek Ebben a fejezetben egy életszerűbb, de egyszerű példán keresztül fogom bemutatni, hogy mennyire megkönnyíti az életünket az Rx használata még az új async-await kulcsszavak mellett is. Egy keresőt fogunk készíteni, mely egy szimulált webszolgáltatással fog dolgozni. Az egyszerűség kedvéért nem egy valódi webszolgáltatáshoz fog kapcsolódni az alkalmazás, hanem egy statikus listából fog javaslatokat és találatokat szolgáltatni mesterséges késleltetéssel és véletlenszerű meghibásodással. Rakjuk össze az alkalmazás alapjait, hogy aztán már csak a lényegre kelljen koncentrálni. Töltsük le és telepítsük fel a legújabb Rx-et a címről. Nyissuk meg a Visual Studio 2012-t. Indítsunk egy új projektet (File New Project), majd a felugró ablakban a baloldali sávban válasszuk ki a Visual C# szekción belül a Windows Store-t, jobboldalon pedig a Blank Application-t. Miután elneveztük a projektet, nyomjunk az Ok gombra. Ezzel van is üres alkalmazásunk. Adjuk hozzá a referenciákhoz az Rx-et. Ezt a Project Add reference menüpontból tudjuk megtenni, a felugró ablakon belül baloldalt válasszuk a Windows fülön belül az Extensions menüpontot, majd rakjunk egy pipát a Reactive Extensions for Windows 8 mellé. Ezzel hozzá is adtuk az összes szükséges dll-t a referenciákhoz. Szerezzünk egy listát a leggyakrabban használt magyar szavakról ezekből fogunk keresési javaslatokat komponálni. Egy ilyen lista található például a következő címen: Dolgozzuk fel ezt a listát úgy, hogy eltűnjenek a sorok elejéről a sorszámok és minden sorban csak egy szó legyen! Az ehhez szükséges kód lényegi részét a kódrészlet mutatja. 9

12 2-1-1 kódrészlet: Wikipediáról kimásolt szöveg megtisztítása var data = rawdata.split(new string[] Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.substring(x.indexof('.') + 1).Replace(" ", "").Split(',')).SelectMany(x => x).aggregate((accumulator, actual) => accumulator + Environment.NewLine + actual); A következő lépés az ál-webszolgáltatás elkészítése. Két metódusra lesz szükség. Az egyik keresési javaslatokat fog generálni egy megadott szövegre úgy, hogy megpróbálja az utolsó szót az imént feldolgozott lista alapján valami értelmesre kiegészíteni. A másik metódus pedig statikusan visszaadja, hogy mire kerestünk a következő formában: Erre kerestél szöveg} Az ezt a funkcionalitást megvalósító osztály alapját a kódrészlet mutatja. public class SearchService private IEnumerable<string> _wordlist; public SearchService() Initialize(); } kódrészlet: Ál-webszolgáltatás alapja private async void Initialize() var txtfile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///SearchEngine_Core/words.txt")); var txt = await FileIO.ReadTextAsync(txtFile); _wordlist = txt.split(new string[] Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.tolower()); } private async Task SimulateTimeOutAndFail() // Simulating long time execution var random = new Random(); await Task.Delay(random.Next(300)); } } // Simulating failure if (random.next(100) < 10) throw new Exception("Error!"); 10

13 Az Initialize() metódus felel azért, hogy a szövegfájl betöltődjön, és aztán készíthessünk belőle javaslatokat, a SimulateTimeOutAndFail() beszédes nevű metódus pedig egy webszolgáltatás viselkedését próbálja szimulálni a mesterséges késleltetéssel és a véletlenszerű meghibásodással. A következő lépés a két lényegi metódus elkészítése. A találatot adó metódus nagyon egyszerű lesz, csak vissza kell adnia az átadott szöveget megformázva (2-1-3 kódrészlet) kódrészlet: Keresés eredményének visszaadása public async Task<IEnumerable<string>> GetResultsForQuery(string query) await SimulateTimeOutAndFail(); } return new string[] "Erre kerestél: " + query}; A javaslatok szolgáltatása már egy fokkal trükkösebb, ezt a kódrészlet mutatja kódrészlet: Keresési javaslatok nyújtása public async Task<IEnumerable<string>> GetSuggestionsForQuery(string query) await SimulateTimeOutAndFail(); if (_wordlist!= null) var wordsofquery = query.tolower().split(new char[] ' ' }, StringSplitOptions.RemoveEmptyEntries); var lastwordofquery = wordsofquery.last(); var suggestionsforlastword = _wordlist.where(w => w.startswith(lastwordofquery)); var headofquery = ""; if (wordsofquery.length > 1) headofquery = wordsofquery.take(wordsofquery.length - 1).Aggregate((acc, curr) => acc + ' ' + curr); } return suggestionsforlastword.select( s => headofquery + ' ' + s).take(10); } else return Enumerable.Empty<string>(); 11

14 Kicsomagoljuk a beírt szövegből az utolsó szót Kikeressük a lehetséges javaslatokat erre azokat a szavakat a hosszú listánkban, ami azzal a karakterlánccal kezdődik Ez után összerakjuk az eredeti szöveget úgy, hogy az utolsó szót (amihez épp javaslatokat gyűjtöttük) kihagyjuk belőle Utolsó lépésként pedig generálunk egy listát, ami a teljes szöveget tartalmazza az összes lehetséges végződéssel Utolsó lépésként készítsük el az alkalmazás felületét. Helyezzünk el a felületen egy TextBox, egy Button, egy ListView és egy TextBlock vezérlőt és adjuk nekik rendre a SearchBox, SearchButton, Suggestions és ErrorLabel neveket. Ezt a kódrészlet szemlélteti. 12

15 2-1-5 kódrészlet: Az alkalmazás felületének XAML kódja <Page x:class="searchengine_rx.mainpage" xmlns= xmlns:x= xmlns:local="using:searchengine_rx" xmlns:d= xmlns:mc= mc:ignorable="d"> <Page.BottomAppBar> <AppBar IsOpen="True" IsSticky="True"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="20" /> <ColumnDefinition /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBox x:name="searchbox" VerticalAlignment="Center" Grid.Column="1"> <TextBox.RenderTransform> <TranslateTransform Y="-10" /> </TextBox.RenderTransform> </TextBox> <TextBlock x:name="errorlabel" Text="Status" Grid.Column="1" Style="StaticResource BasicTextStyle}" FontSize="12" VerticalAlignment="Center"> <TextBlock.RenderTransform> <TranslateTransform Y="23" /> </TextBlock.RenderTransform> </TextBlock> <Button x:name="searchbutton" Grid.Column="2" Style="StaticResource SearchAppBarButtonStyle}" /> </Grid> </AppBar> </Page.BottomAppBar> <Grid Background="StaticResource ApplicationPageBackgroundThemeBrush}"> <ListView x:name="suggestions" Margin="10" Grid.Row="1" SelectionMode="None" IsItemClickEnabled="True"> <ListView.ItemTemplate> <DataTemplate> <TextBlock FontSize="24" Text="Binding}" /> </DataTemplate> </ListView.ItemTemplate> </ListView> </Grid> </Page> Ezzel meg is van az álszerviz osztály és a felület, most már koncentrálhatunk a lényegre. 13

16 Hagyományos megközelítés Most, hogy lehelyeztük az alkalmazás alapköveit, vegyük át, hogy mit szeretnénk megvalósítani. Szeretnénk, ha annak hatására, hogy beleír valamit a felhasználó a keresődobozba, elkezdene az alkalmazás nagy erőkkel keresési javaslatokat dobálni neki. Emellett pedig szeretnénk, ha a Keress gombra kattintva már egy valódi keresés valódi találatait adná vissza nekünk. Kis magyarázat az idézőjelekre: a weben található keresőmotorok nem a valódi lehetséges találatok alapján adnak keresési javaslatokat, a javaslatok a felhasználók által beírt kifejezésekből tevődnek össze, míg a találatok már valóban azok az oldalak, melyek a beírt kifejezést valamilyen formában tartalmazzák. Tehát attól még, hogy a kereső egy kifejezésre előhoz nekünk egy keresési javaslatot, az nem jelenti azt, hogy arra fog bármit is találni. Mivel nem egy bonyolult programról van szó, készítsük el a hagyományos módon az alkalmazást, és keressük meg benne a hibákat. Rakjuk bele az oldal konstruktorába a kódrészletet, és próbáljuk ki működés közben kódrészlet: Feliratkozás a felület eseményeire SearchBox.TextChanged += async (s, e) => var query = SearchBox.Text; var service = new SearchService(); var suggestions = await service.getsuggestionsforquery(query); Suggestions.ItemsSource = suggestions; }; SearchButton.Click += async (s, e) => var query = SearchBox.Text; var service = new SearchService(); var results = await searchservice.getresultsforquery(query); Suggestions.ItemsSource = results; }; Mi történik az alkalmazás futása közben? A véletlenszerű késleltetésnek köszönhetően valószínűleg pillanatok alatt szembetaláljuk magunkat azzal a helyzettel, hogy beírjuk, hogy Buda, de mivel minden egyes új karakter leütésekor kérünk az aktuális beírt szövegre keresési javaslatot ami valamilyen késleltetéssel tér vissza megeshet, hogy az első karakterhez ( B ) kapcsolódó javaslatok akkor jönnek meg, mikor már rég beírtuk, hogy Buda, így aztán nem azokat a szavakat fogjuk látni amik Buda -val kezdődnek, hanem azokat, amik B -vel. Emellett, ha ez valóban egy webszolgáltatás hívása lenne, akkor gyakorlatilag feleslegesen küldenénk iszonyatos mennyiségű lekérést a szolgáltatás felé minden egyes leütött karakter után. Ezzel felesleges 14

17 hálózati forgalmat generálunk, ami feleslegesen eszi a felhasználó akkumulátorát, processzorát és adott esetben a fizetős 3G adatforgalmát. Látván a problémákat (azoknak egy részét), gondoljuk át mire van szükségünk. Szeretnénk a szervizhívást hibabiztosra elkészíteni, azaz számolnunk kell azzal, hogy a szerveren történik valami hiba, vagy a kliensről nem tud kimenni a hívás, vagy túl hosszú ideig tart a művelet. Mindezt, annak érdekében, hogy hibatűrő legyen érdemes becsomagolni úgy, hogy akármilyen hiba is történik, próbálja újra a rendszer még kétszer (összesen maximum háromszor) a hívást. Jó volna, ha lenne egy kis fojtás a hívó oldalon, és nem küldenénk el egy-egy hívást minden egyes leütött karakter után, hanem csak akkor, ha már a felhasználó legalább fél másodperce nem nyomott le billentyűt. Az előzőhöz képest apró optimalizálás, de érdemes arra is figyelni, hogy ha valami okból kifolyólag kétszer egymásután ugyanarra a szövegre szeretne keresni, akkor feleslegesen ne küldjük el másodjára is a hívást. Ilyen akkor lehet, ha beírt valamit a felhasználó, várt egy keveset, beírt még valamit, de közben rájött, hogy mégsem az kell neki és visszatörölte az előző állapotig a beírt szöveget. A fentebb említett fojtás miatt lehet, hogy kétszer egymás után ugyanazt a szöveget kapnánk meg. Vagy egyszerűen csak remegő kézzel nyom a Keresés gombra vagy üt az Enter billentyűre és ezért véletlenül többször is elküldi a hívást. Végül pedig meg kell oldani a versenyhelyzet problémát, azaz, hogy biztosítsuk, hogy valóban mindig az utolsó hívás eredményét lássuk a képernyőn és ne történhessen meg, hogy egy korábban elküldött, de a szerveren hosszabb ideig tartó lekérdezés eredményét később visszakapva inkonzisztenciát okozzunk a felhasználónak, és olyan javaslatokat mutassunk, aminek már semmi köze nincs az éppen beírt keresendő szöveghez. Vegyük ezeket a problémákat egyesével, nézzük meg hogyan lehet megoldani őket, majd azt, hogy miképpen lehet összekombinálni őket. Időtúllépés A C# 5.0-ban bevezetett await kulcsszónak köszönhetően olyan egyszerű, átlátható és rövid kódot tudunk aszinkron műveletekre írni, mintha csak szinkron kódot írnánk. A szervizhívás önmagában csupán egy sor kódrészlet: await kulcsszó használata aszinkron hívásnál var suggestions = await service.getsuggestionsforquery(query); ám amint egy kicsit bonyolultabb műveletet akarunk ezzel az aszinkron hívással végezni, kénytelenek vagyunk elhagyni ezt a kényelmet. Az időtúllépés logikát a legegyszerűbben a kódrészlettel lehet megvalósítani: 15

18 2-2-3 kódrészlet: Időtúllépés implementációja public async Task<IEnumerable<string>> ServiceCall_Timeout(string query) var newtask = service.getsuggestionsforquery(query); var timeouttask = Task.Delay(500); var firsttasktoend = await Task.WhenAny(newTask, timeouttask); } if (newtask == firsttasktoend) return newtask.result; else throw new Exception("Timeout"); Elindítjuk a saját szervizhívásunkat és egy egyszerű aszinkron késleltetést párhuzamosan, megvárjuk amíg valamelyik visszatér, majd leellenőrizzük, hogy a szervizhívás vagy az időzítő tért vissza, azaz, hogy a szervizhívás kifutott e az időből. Újrapróbálás Bár az await kulcsszó megint sokat segít, de a komolyabb kódolás elkerülhetetlen kódrészlet: Újrapróbálás implementációja public async Task<IEnumerable<string>> ServiceCall_Retry(string query) bool errorhappened = false; int tries = 0; IEnumerable<string> suggestions = null; do errorhappened = false; try suggestions = await service.getsuggestionsforquery(query); } catch tries++; errorhappened = true; } } while (errorhappened == true && tries < 3); } if (!errorhappened) return suggestions; else throw new Exception("Out of retries"); Mivel újrapróbálásról van szó, ezért a kiinduló gondolat az kell, hogy legyen, hogy valamilyen ciklust kell készíteni. Mivel értelemszerűen csak akkor kell újrapróbálni, ha valami probléma történt, viszonylag gyorsan leszűkül a kör a while ciklus használatára. Innentől kezdve ízlés kérdése, hogy az 16

19 elől vagy hátul tesztelő verziót használjuk, én az utóbbit választottam. Két feltétele van annak, hogy a ciklusban maradjunk. Az egyik, hogy valami hiba történjen a cikluson belül, a másik pedig, hogy még a megadott próbák száma alatt legyünk (jelenleg ez 3). A hibát egy try-catch blokkal elnyeljük és jelezzük a ciklusnak, hogy hiba történt és növeljük az próbálkozások számát. Érezhető, hogy ebből még nem tudunk tisztán következtetést levonni, mert azért is túljuthatunk a cikluson, mert nem történt probléma, és azért is, mert már háromszor is volt baj. Tehát le kell ellenőriznünk, hogy volt e hiba vagy nem. A fojtás, az egyezés vizsgálat és a versenyhelyzet kezelése már nem ilyen egyszerű feladat. Ezek szűrő jellegű műveletek, azaz különböző feltételektől függően elnyelnek bizonyos hívásokat. A fojtás például biztosítja, hogy egy gyors gépelésnél ne küldjünk hívást minden egyes billentyűleütésre, hanem csak akkor, ha már a felhasználó fél másodperce nem nyúlt a billentyűzethez. Az egyezés vizsgálat kiszűri az egymás után érkező azonos paraméterű hívásokat. A versenyhelyzet kezelés pedig biztosítja, hogy a korábbi, ám hosszú ideig tartó hívás eredménye már ne kerüljön felszínre, amikor van nála frissebb. Ezeknél a műveleteknél tehát egy olyan mintát fogunk követni, hogy lesz egy void metódus és mellette egy CallBack esemény, amiben az eredményeket kapjuk. Az eseményre csak egyszer fogunk feliratkozni, a metódust azonban értelemszerűen többször hívjuk meg. Fojtás private DateTime lastthrottledparameterdate; private int throttleinterval = 500; kódrészlet: Fojtás implementációja public event Action<IEnumerable<string>> CallBack_Throttle; public async void ServiceCall_Throttle(string query) lastthrottledparameterdate = DateTime.Now; await Task.Delay(throttleInterval); if ((DateTime.Now - lastthrottledparameterdate).totalmilliseconds < throttleinterval) return; var suggestions = await service.getsuggestionsforquery(query); } if (CallBack_Throttle!= null) CallBack_Throttle(suggestions); A metódus hívásakor elmentjük az aktuális időpontot, kivárjuk azt a bizonyos fél másodpercet, majd megvizsgáljuk, hogy az új aktuális időpont és a korábban elmentett között megvan e a fél másodperc 17

20 különbség, azaz, hogy nem hívták e meg idő közben a metódust még egyszer. Ha nem, akkor elküldjük a szervizhívást, majd az eredményével meghívjuk a CallBack eseményt. Egyezés vizsgálat private string lastdistinctparameter; kódrészlet: Egyezés vizsgálat implementációja public event Action<IEnumerable<string>> CallBack_Distinct; public async void ServiceCall_Distinct(string query) if (lastdistinctparameter!= query) lastdistinctparameter = query; else return; var suggestions = await service.getsuggestionsforquery(query); } if (CallBack_Distinct!= null) CallBack_Distinct(suggestions); Ennél a műveletnél egy, a tulajdonságok setterében is gyakran használt módszert alkalmazunk. A metódus hívásakor kimentjük a paraméterét egy globális változóba, majd a következő hívásnál ehhez hasonlítjuk az új paramétert és csak akkor engedjük végre a végrehajtást, ha különböznek. Versenyhelyzet kezelése kódrészlet: Versenyhelyzet kezelésének egy lehetséges implementációja private Task<IEnumerable<string>> lastcall; public event Action<IEnumerable<string>> CallBack_RaceCondition; public async void ServiceCall_RaceCondition(string query) var newcall = service.getsuggestionsforquery(query); lastcall = newcall; await newcall; } if (lastcall == newcall) if (CallBack_RaceCondition!= null) CallBack_RaceCondition(newCall.Result); 18

21 Ennél a műveletnél megint a Task-okat használjuk fel és az await kulcsszó megfelelő helyen történő felhasználásával trükközünk. A csomagolómetódus meghívásakor elmentjük a szervizhíváshoz tartozó Task objektumot, bevárjuk a hívás eredményét, majd megvizsgáljuk, hogy a kimentett lastcall Task példány még mindig megegyezik e a bevárt newcall Task példánnyal. Ezzel azt tudjuk megvizsgálni, hogy amíg a szervizhívásunk futott, nem esett e be egy újabb hívás, mely érvényteleníti a korábbi hívást. Ezzel tudjuk biztosítani, hogy az utoljára visszakapott eredmény az valóban az utolsó híváshoz tartozzon. Ezek a műveletek egyenként is jó pár sort elfoglalnak, és sajnos az összekapcsolásuk nem oldható meg könnyen, szóval csak elrettentésképpen szemléltetném a kódrészlettel, hogy ezek a funkciók összegyúrva hogyan néznének ki: 19

22 // Throttle global variables private DateTime lastthrottledparameterdate; private int throttleinterval = 500; // Distinct global variables private string lastdistinctparameter; // Retry global variables private int retries = 3; // Timeout global variables private int timeoutinterval = 500; // Switch global variables private Task<string> lastcall; kódrészlet: A hagyományos megközelítés // Callback events public event Action<IEnumerable<string>> CallBack; public event Action<Exception> ErrorCallBack; public async void GetQuerySuggestionsAsync(string query) try // Throttle logic lastthrottledparameterdate = DateTime.Now; await Task.Delay(throttleInterval); if ((DateTime.Now - lastthrottledparameterdate).totalmilliseconds < throttleinterval) return; // Distinct logic if (lastdistinctparameter!= query) lastdistinctparameter = query; else return; 20

23 var newcall = Task.Run<IEnumerable<string>>(async () => // Retry logic bool errorhappened = false; int tries = 0; IEnumerable<string> suggestions = null; do errorhappened = false; try // Timeout logic var newtask = service.getsuggestionsforquery(query); var timeouttask = Task.Delay(timeoutInterval); var firsttasktoend = await Task.WhenAny(newTask, timeouttask); if (newtask == firsttasktoend) suggestions = newtask.result; else throw new Exception("Timeout"); } catch tries++; errorhappened = true; } } while (errorhappened == true && tries < retries); if (!errorhappened) return suggestions; else throw new Exception("Out of retries"); }); // Switch logic lastcall = newcall; await newcall; } if (lastcall == newcall) if (CallBack!= null) CallBack(newCall.Result); } catch (Exception ex) if (ErrorCallBack!= null) ErrorCallBack(ex); } 21

24 Látható tehát, hogy annak ellenére, hogy nem is szállt el annyira a fantáziánk, rengeteget kellett kódolnunk ezekért az alapvetőnek vehető feladatokért is. Ha ezen túljutottunk, akkor viszont a felhasználás már egész kényelmesnek mondható, ezt a kódrészlet szemlélteti kódrészlet: A as kód felhasználása public MainPage() // Initialization this.initializecomponent(); this.loaded += (s, e) => SearchBox.Focus(FocusState.Keyboard); var service = new SearchService(); // Helper method var callback = new Action<CustomGenericAsyncResult<IEnumerable<string>>>(e => if (e.exception!= null) ErrorLabel.Text = e.exception.message; else Suggestions.ItemsSource = e.result; }); // Suggestions var getsuggestionswrapper = new CustomGenericAsyncCall <string, IEnumerable<string>>(service.GetSuggestionsForQuery); SearchBox.TextChanged += (s, e) => getsuggestionswrapper.wrappedcallasync(searchbox.text); getsuggestionswrapper.callback += callback; // Results var getresultswrapper = new CustomGenericAsyncCall <string, IEnumerable<string>>(service.GetResultsForQuery); SearchButton.Click += (s, e) => getresultswrapper.wrappedcallasync(searchbox.text); Suggestions.ItemClick += (s, e) => getresultswrapper.wrappedcallasync(e.clickeditem as string); SearchBox.KeyDown += (s, e) => if (e.key == VirtualKey.Enter) getresultswrapper.wrappedcallasync(searchbox.text); }; }. getresultswrapper.callback += callback; 22

25 Rx megközelítés Keresési javaslatok Rx-nél mindig adatfolyamokban, csővezetékekben kell gondolkozni aminek van egy eleje ( forrása ), és egy vége ahol kiesik belőle az adat. Közben pedig az adatfolyamot megfigyelhetjük, manipulálhatjuk, szűrhetjük, késleltethetjük stb. Jelen esetben a célunk az, hogy ha a felhasználó beír valamit a keresődobozba, induljon el egy szervizhívás javaslatokért, majd az eredménye jelenjen meg a felületen. Azaz a forrás a szövegdoboz, az szolgáltatja a szervizhívás számára a bemeneti paramétereket. Ahhoz, hogy egy eseményből adatfolyamot készítsünk, az Observable típus statikus FromEventPattern() metódusát fogjuk használni kódrészlet: Esemény-feliratkozás Rx módra var querytextchanged = Observable.FromEventPattern(SearchBox, "TextChanged"); Ezen a ponton egy IObservable<EventPattern<object>> típusú adatfolyamunk van, azaz egy olyan lista ami EventPattern<object> típusú elemeket tartalmaz. Ez egy csomagolótípus a hagyományos (object sender, object args) szignatúrájú eseményekre. A TextBox vezérlő TextChanged eseményében az argumentum TextChangedEventArgs típusú, ez azonban sajnos nem tartalmazza a szövegdoboz aktuális tartalmát, így sok hasznát nem tudjuk venni. Jelenleg tehát van egy adatfolyamunk, amiben megjelenik egy elem minden alkalommal, amikor a felhasználó leüt egy billentyűt. A feladat hát az, hogy ezeket az elemeket valami használható információvá alakítsuk. Ezt a Select() operátorral fogjuk tudni megtenni kódrészlet: Esemény-feliratkozás Rx módra, LINQ-val tálalva var querytextchanged = Observable.FromEventPattern(SearchBox, "TextChanged").Select(e => SearchBox.Text); Most már egy olyan adatfolyamunk van, amiben megjelenik a szövegdoboz aktuális tartalma minden alkalommal, amikor a felhasználó beleír valamit. Jöhet a fojtás és az ismétlődések szűrése. 23

26 2-3-3 kódrészlet: Fojtás és egyezés vizsgálat var querytextchanged = Observable.FromEventPattern(SearchBox, "TextChanged").Select(e => SearchBox.Text).Throttle(TimeSpan.FromMilliseconds(100)).DistinctUntilChanged(); A bemeneti paraméter adatfolyama után a következő feladat a szervizhívás. Ez két részből fog állni. Fel kell vértezni magát a szervizhívást Timeout és Retry logikával, majd bele kell fűzni a querytextchanged adatfolyamba. Egy aszinkron hívást a legegyszerűbben a ToObservable() operátorral tudjuk adatfolyammá alakítani kódrészlet: Aszinkron művelet Rx adatfolyammá konvertálása var observablesuggestions = service.getsuggestionsforquery("x").toobservable(); Ezzel kapunk egy IObservable<IEnumerable<string>> típusú adatfolyamot, melyben a szervizhívás eredménye fog megjelenni vagy rosszabb esetben egy hibaüzenet, hogy valami hiba történt. Ezt az esetleges hibát, és az időtúllépés esetleges problémáját a Timeout() és a Retry() operátorokkal tudjuk orvosolni. A Timeout() hibát dob, ha az adott hívás kifut az időből, míg a Retry() hiba esetén újrapróbálja a hívást. A Retry() elkapja azt is, ha maga a szervizhívás dob hibát és azt is, ha a Timeout() operátor. Mindezt a kódrészlet szemlélteti kódrészlet: Időtúllépés és újrapróbálás var observablesuggestions = service.getsuggestionsforquery("x").toobservable().timeout(timespan.frommilliseconds(250)).retry(3); A következő lépés, hogy mindezt belefűzzük az eredeti adatfolyamba, és kiváltsuk a szervizhívás jelenlegi példában szereplő X paraméterét. Ahhoz, hogy átlátható maradjon a kód, érdemes egy segédmetódust létrehozni, ami az iménti csomagolást elvégzi egy tetszőleges aszinkron hívásra. 24

27 2-3-6 kódrészlet: Csomagoló funkció var wrapasynccall = new Func< Func<string, Task<IEnumerable<string>>>, string, IObservable<IEnumerable<string>>>( (asynccall, parameter) => return asynccall(parameter).toobservable().timeout(timespan.frommilliseconds(250)).retry(3); }); Egy kicsit sok a generikus típusparaméter, menjünk rajtuk végig sorjában. A Func<R> típus egy olyan delegáltnak felel meg, ami nem kap paramétert, és a visszatérési típusa R. Van neki azonban még nagyon sok verziója, amikkel a paraméterek típusát tudjuk megadni, azaz egy Func<P, R> egy olyan metódusnak felel meg, ami vár egy P típusú paramétert, a visszatérési típusa pedig R. És ezt lehet fokozni 16 bemeneti paraméterig (P1, P2, P3, ). A jelenlegi példában 3 típusparaméter található, 2 paraméter és a visszatérési típus. Az első típusparaméter egy újabb Func, ami újabb magyarázat nélkül egy string típusú paramétert vár, a visszatérési típusa pedig Task<IEnumerable<string>>, azaz egy aszinkron metódusról van szó, esetünkben ez a szervizhívás lesz. A második típusparaméter egy string. Ezt fogjuk paraméterül átadni az előző paraméterként átadott delegáltnak. A harmadik típusparaméter, azaz a visszatérési típus, pedig az IObservable<IEnumerable<string>>. Ha nem lenne itt a kód többi része, akkor is sejteni lehetne, hogy az első paraméterként átadott aszinkron hívást fogjuk adatfolyammá alakítani. A kód többi része már csak egy Lambda kifejezés, ami ezt a csomagoló metódust reprezentálja. Láthatóan két paramétert kap (név szerint az asynccall és parameter nevű paramétereket), és egy adatfolyamot ad vissza. Látható, hogy a paraméterül kapott aszinkron metódust meghívjuk a szintén paraméterül kapott paraméterrel, majd adatfolyammá alakítjuk a ToObservable() operátorral, és ráakasztjuk a Timeout() és Retry() operátorokat is. A sok szenvedés végül meghozza a gyümölcsét, most már szépen egyetlen sorban bele tudjuk fűzni a felokosított szervizhívást a keresődoboz adatfolyamába kódrészlet: Csomagoló funkció felhasználása var observablesuggestions = querytextchanged.select(q => wrapasynccall( service.getsuggestionsforquery, q)); 25

28 A befűzés a Select() operátorral kezdődik, ám nem azzal fog véget érni. Ez a két sor kód azt eredményezi, hogy minden billentyűlenyomásra (persze lefojtva, megszűrve) elindítunk egy szervizhívást az aktuális tartalmával a keresőmezőnek. Ezzel azonban azt értük el, hogy van egy adatfolyamunk, amin további adatfolyamok jelennek meg, amikben majd végül megjelenik az egyes szervizhívások eredménye. A cél az volna, hogy megszüntessük ezt az adatfolyam az adatfolyamban állapotot és valahogy csak a legutolsó hívás által generált adatfolyamra figyeljünk, ezzel megszüntetve az egyes adatfolyamokban párhuzamosan megjelenő eredmények versenyét. Szerencsére kifejezetten erre a célra lett kitalálva a Switch() operátor, így nem fog nehezünkre esni, megoldani ezt a problémát kódrészlet: Versenyhelyzet kezelése var observablesuggestions = querytextchanged.select(q => wrapasynccall(service.getsuggestionsforquery, q)).switch(); Mostanra van egy adatfolyamunk, ami már lényegében mindent tartalmaz, amire csak szükségünk volt. Lefojtottuk és megszűrtük az ismétlődésektől az inputot, és felokosítottuk a szervizhívást. Az utolsó lépés, hogy végre a csővezeték végére álljunk, és megjelenítsük az eredményt. Normál esetben erre a célra a Subscribe() metódust használnánk, azonban itt ezt nem tehetjük meg. Az Rx-es adatfolyamok 3 csatornából állnak: OnNext, OnError és OnCompleted. OnNext eseményből bármennyi lehet egy adatfolyamban élete során, OnError vagy OnCompleted eseményből azonban csak egy. Ha valami hiba történik az adatfolyamban, akkor végigmegy rajta egy OnError üzenet, és lezárul a cső. Ilyen eset márpedig ebben a példában előfordulhat, és nekünk arra van szükségünk, hogy mégis csak megjelenhessen akármennyi OnError esemény és még se álljon le az adatfolyam. A hibákat a Retry() operátorral fogjuk eliminálni. Ez gondoskodik arról, hogy elnyelje az esetleges hibát és újraindítsa az adatfolyamot. Ahhoz pedig, hogy mégis értesüljünk a hibákról is (még mielőtt a Retry() elnyelné) a Do() operátort fogjuk használni. Ezt az operátort az adatfolyam tetszőleges részére beszúrhatjuk és megfigyelhetjük az adatfolyam aktuális állapotát, beleértve az OnNext, OnError és OnCompleted csatornákat. Megint csak a végleges kód szépítése érdekében érdemes bevezetni két segédmetódust: egyet az OnNext csatorna kezelésére, egyet pedig az OnError-éra. 26

29 2-3-9 kódrészlet: Feliratkozás az OnNext és OnError eseményekre var onnext = new Action<IEnumerable<string>>(values => ErrorLabel.Visibility = Visibility.Collapsed; Suggestions.ItemsSource = values; }); var onerror = new Action<Exception>(error => ErrorLabel.Visibility = Visibility.Visible; ErrorLabel.Text = error.message; }); Ezen két segédmetódus felhasználásával a végleges kódot a kódrészlet mutatja kódrészlet: Végleges Rx kód a keresési javaslatokra var querytextchanged = Observable.FromEventPattern(SearchBox, "TextChanged").Select(e => SearchBox.Text).Throttle(TimeSpan.FromMilliseconds(100)).DistinctUntilChanged(); var observablesuggestions = querytextchanged.select(q => wrapasynccall( service.getsuggestionsforquery, q)).switch().observeondispatcher().do(onnext, onerror).retry(); observablesuggestions.subscribe(); Az ObserveOnDispatcher() operátorra azért van szükség, hogy az utána következő műveletek a Dispatcher-en, azaz a UI szálon fussanak. A Subscribe() hívásra azért van szükség, mert azzal indítjuk be az egész adatfolyamot. A feliratkozás alulról indul el, azaz amikor egy ilyen hosszú operátorlánc végén meghívjuk a Subscribe() metódust, akkor hátulról kezdve elkezdenek feliratkozni egymásra az operátorok, míg nem a sor elejére érnek, ahol megtörténik a SearchBox TextChanged eseményére a feliratkozás és megindul az adatfolyam. Keresési találatok A keresés találatainak megszerzése nagyon hasonló a fentebb leírtakhoz. A különbség csupán annyi, hogy több esemény hatására is elindulhat a keresés: megnyomja a felhasználó a keresés gombot, vagy entert üt a szövegdobozban, vagy kiválaszt egy javaslatot a listából. 27

30 kódrészlet: Eseményfeliratkozások var searchbuttonclicked = Observable.FromEventPattern(SearchButton, "Click").Select(_ => SearchBox.Text); var enterkeypressed = Observable.FromEventPattern<KeyRoutedEventArgs> (SearchBox, "KeyDown").Where(e => e.eventargs.key == VirtualKey.Enter).Select(_ => SearchBox.Text); var suggestionselected = Observable.FromEventPattern<ItemClickEventArgs>(Suggestions, "ItemClick").Select(e => e.eventargs.clickeditem as string); Ezzel a három adatfolyammal van három azonos típusú forrásunk, melyeken különböző események hatására a keresődoboz tartalma vagy épp egy kiválasztott javaslat jelenik meg. Ezeket a Merge() operátorral tudjuk összefésülni kódrészlet: Különböző eseményforrások összefésülése var observableresults = Observable.Merge(searchButtonClicked, enterkeypressed, suggestionselected).distinctuntilchanged().select(q => wrapasynccall(service.getresultsforquery, q)).switch().observeondispatcher().do(onnext, onerror).retry(); Érdemes megfigyelni, hogy a DistinctUntilChanged() operátort itt az összefésülés után használjuk, azaz ha valamit beír a felhasználó és entert nyom és rányom a keresés gombra is, akkor sem küldünk extra szervizhívást. A teljesség kedvéért most is mellékelem a teljes kódot. A kód itt mindenféle segédosztályok nélkül teljesen a XAML mögöttes kódjában, azon belül is az oldal konstruktorában helyezkedik el. A kód itt is az inicializációval kezdődik kódrészlet: Inicializáció public MainPage() // Initialization this.initializecomponent(); this.loaded += (s, e) => SearchBox.Focus(FocusState.Keyboard); var service = new SearchService(); 28

Szoftvertechnolo gia gyakorlat

Szoftvertechnolo gia gyakorlat Szoftvertechnolo gia gyakorlat Dr. Johanyák Zsolt Csaba http://johanyak.hu 1. Dependency Injection (függőség befecskendezés) tervezési minta A tervezési minta alapgondolata az, hogy egy konkrét feladatot

Részletesebben

Delphi programozás I.

Delphi programozás I. Delphi programozás I. Konzol alkalmazások készítése Delphiben A Delphi konzol alkalmazása (console application) olyan 32 bites program, amely nem grafikus felületen, hanem egy szöveges konzol ablakban

Részletesebben

Concurrency in Swing

Concurrency in Swing Concurrency in Swing A szálkezelés a swing alkalmazásokban is fontos. Cél egy olyan felhasználói felület készítése, amely soha nem fagy, mindig válaszol a felhasználói interakciókra, bármit is csináljon

Részletesebben

Java és web programozás

Java és web programozás Budapesti Műszaki Egyetem 2015. 04. 08. 9. Előadás Kivétel kezelés a kivétel (exception) egy esemény, mely futás közben megbontja a program normális futási folyamatát például kivétel dobódik amikor 0-val

Részletesebben

Osztályok. 4. gyakorlat

Osztályok. 4. gyakorlat Osztályok 4. gyakorlat Az osztály fogalma Az objektumok formai leírása, melyek azonos tulajdonsággal és operációkkal rendelkeznek. Osztályból objektum készítését példányosításnak nevezzük. Minden objektum

Részletesebben

Miután létrehoztuk, szeretnénk neki beszédesebb nevet adni. A név változtatásához a következőt kell tenni:

Miután létrehoztuk, szeretnénk neki beszédesebb nevet adni. A név változtatásához a következőt kell tenni: Excel objektumok Az excelben az osztályokat úgynevezett class modulokként hozzuk létre. Miután létrehoztuk, szeretnénk neki beszédesebb nevet adni. A név változtatásához a következőt kell tenni: View-ba

Részletesebben

1. Jelölje meg az összes igaz állítást a következők közül!

1. Jelölje meg az összes igaz állítást a következők közül! 1. Jelölje meg az összes igaz állítást a következők közül! a) A while ciklusban a feltétel teljesülése esetén végrehajtódik a ciklusmag. b) A do while ciklusban a ciklusmag után egy kilépési feltétel van.

Részletesebben

Vizuális és eseményvezérelt programozás , II. félév BMF NIK

Vizuális és eseményvezérelt programozás , II. félév BMF NIK Vizuális és eseményvezérelt programozás 2006 2007, II. félév BMF NIK Eseménykezelés A képviselő( delegate ) Képviselők C# nyelvi megvalósítása Metódushívás képviselőn keresztül Az esemény ( event ) Esemény

Részletesebben

Webprogramozás szakkör

Webprogramozás szakkör Webprogramozás szakkör Előadás 5 (2012.04.09) Programozás alapok Eddig amit láttunk: Programozás lépései o Feladat leírása (specifikáció) o Algoritmizálás, tervezés (folyamatábra, pszeudokód) o Programozás

Részletesebben

Adabáziselérés ODBC-n keresztül utasításokkal C#-ban

Adabáziselérés ODBC-n keresztül utasításokkal C#-ban Adabáziselérés ODBC-n keresztül utasításokkal C#-ban 1. Előkészítés Access adatbázis lemásolása, ODBC DSN létrehozása Másoljuk le az alábbiakat: Mit Honnan Hova list.mdb p:\johanyák Csaba\Vizualis programozas\data\

Részletesebben

Kivételkezelés, beágyazott osztályok. Nyolcadik gyakorlat

Kivételkezelés, beágyazott osztályok. Nyolcadik gyakorlat Kivételkezelés, beágyazott osztályok Nyolcadik gyakorlat Kivételkezelés Nem minden hibát lehet fordítási időben megtalálni Korábban (pl. C-ben) a hibakezelést úgy oldották meg, hogy a függvény hibakódot

Részletesebben

Java Programozás 9. Gy: Java alapok. Adatkezelő 5.rész

Java Programozás 9. Gy: Java alapok. Adatkezelő 5.rész Java Programozás 9. Gy: Java alapok Adatkezelő 5.rész 15/1 B ITv: MAN 2018.04.22 A Keresés funkció Programlogika: 1. A keresés az etm táblamodellben fog keresni, és a találat rekordokat átmásolja egy másik

Részletesebben

Java programozási nyelv 4. rész Osztályok II.

Java programozási nyelv 4. rész Osztályok II. Java programozási nyelv 4. rész Osztályok II. Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2005. szeptember A Java programozási nyelv Soós Sándor 1/17 Tartalomjegyzék

Részletesebben

Objektum Orientált Programozás. 11. Kivételkezelés 44/1B IT MAN

Objektum Orientált Programozás. 11. Kivételkezelés 44/1B IT MAN Objektum Orientált Programozás 11. Kivételkezelés 44/1B IT MAN B IT v: 2016.05.03 MAN Pici elmélet A Java kivételkezelésének célja a programfutás során keletkezett hibák kiszűrése és megfelelő kezelése.

Részletesebben

Az osztályok csomagokba vannak rendezve, minden csomag tetszőleges. Könyvtárhierarhiát fed: Pl.: java/util/scanner.java

Az osztályok csomagokba vannak rendezve, minden csomag tetszőleges. Könyvtárhierarhiát fed: Pl.: java/util/scanner.java Függvények, csomagok Csomagok Az osztályok csomagokba vannak rendezve, minden csomag tetszőleges számú osztályt tartalmazhat Pl.: java.util.scanner Könyvtárhierarhiát fed: Pl.: java/util/scanner.java Célja:

Részletesebben

Már megismert fogalmak áttekintése

Már megismert fogalmak áttekintése Interfészek szenasi.sandor@nik.bmf.hu PPT 2007/2008 tavasz http://nik.bmf.hu/ppt 1 Témakörök Polimorfizmus áttekintése Interfészek Interfészek kiterjesztése Eseménykezelési módszerek 2 Már megismert fogalmak

Részletesebben

A C# programozási nyelv alapjai

A C# programozási nyelv alapjai A C# programozási nyelv alapjai Tisztán objektum-orientált Kis- és nagybetűket megkülönbözteti Ötvözi a C++, Delphi, Java programozási nyelvek pozitívumait.net futtatókörnyezet Visual Studio fejlesztőkörnyezet

Részletesebben

Programozás alapjai gyakorlat. 4. gyakorlat Konstansok, tömbök, stringek

Programozás alapjai gyakorlat. 4. gyakorlat Konstansok, tömbök, stringek Programozás alapjai gyakorlat 4. gyakorlat Konstansok, tömbök, stringek Házi ellenőrzés (f0069) Valósítsd meg a linuxos seq parancs egy egyszerűbb változatát, ami beolvas két egész számot, majd a kettő

Részletesebben

Java programozási nyelv 5. rész Osztályok III.

Java programozási nyelv 5. rész Osztályok III. Java programozási nyelv 5. rész Osztályok III. Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2005. szeptember A Java programozási nyelv Soós Sándor 1/20 Tartalomjegyzék

Részletesebben

Java Programozás 1. Gy: Java alapok. Ismétlés ++

Java Programozás 1. Gy: Java alapok. Ismétlés ++ Java Programozás 1. Gy: Java alapok Ismétlés ++ 24/1 B ITv: MAN 2018.02.18 Feladat Készítsünk egy komplett konzolos alkalmazást, mely generál egy számot 0 és 100 között (mindkét határt beleértve), feladatunk

Részletesebben

Programozás C++ -ban 2007/7

Programozás C++ -ban 2007/7 Programozás C++ -ban 2007/7 1. Másoló konstruktor Az egyik legnehezebben érthető fogalom C++ -ban a másoló konstruktor, vagy angolul "copy-constructor". Ez a konstruktor fontos szerepet játszik az argumentum

Részletesebben

Objektum orientált kiterjesztés A+ programozási nyelvhez

Objektum orientált kiterjesztés A+ programozási nyelvhez Szegedi Tudományegyetem Informatikai Tanszékcsoport Objektum orientált kiterjesztés A+ programozási nyelvhez Diplomamunka terve Készítette: Bátori Csaba programtervező matematikus hallgató Témavezető:

Részletesebben

Informatika terméktervezőknek

Informatika terméktervezőknek Informatika terméktervezőknek C# alapok Névterület (namespace) using Osztály (class) és Obejtumok Metódus (function, procedure, method) main() static void string[] arg Szintaxis // /* */ \n \t Névadások

Részletesebben

Java Programozás 4. Gy: Java GUI. Tipper, MVC kalkulátor

Java Programozás 4. Gy: Java GUI. Tipper, MVC kalkulátor Java Programozás 4. Gy: Java GUI Tipper, MVC kalkulátor 15/1 B ITv: MAN 2018.03.10 1. Feladat: Tipper Készítsük el a tippelős programunk grafikus változatát. Az üzleti logika kódja megvan, a felület pedig

Részletesebben

Programozás BMEKOKAA146. Dr. Bécsi Tamás 8. előadás

Programozás BMEKOKAA146. Dr. Bécsi Tamás 8. előadás Programozás BMEKOKAA146 Dr. Bécsi Tamás 8. előadás Visszatekintés A Windows Console alkalmazások egy karakteres képernyőt biztosítottak, ahol a kimenet a kiírt szöveg, míg a bemenet a billentyűzet volt.

Részletesebben

A gyakorlat során az alábbi ábrán látható négy entitáshoz kapcsolódó adatbevitelt fogjuk megoldani.

A gyakorlat során az alábbi ábrán látható négy entitáshoz kapcsolódó adatbevitelt fogjuk megoldani. Vizuális programozás 1. A gyakorlat célja A gyakorlat célja a Könyvtár alkalmazás folytatása az előző gyakorlaton elkészített grafikus felület felhasználásával. Elsőként lemásoljuk az előző gyakorlat eredményeként

Részletesebben

Interfészek. PPT 2007/2008 tavasz.

Interfészek. PPT 2007/2008 tavasz. Interfészek szenasi.sandor@nik.bmf.hu PPT 2007/2008 tavasz http://nik.bmf.hu/ppt 1 Témakörök Polimorfizmus áttekintése Interfészek Interfészek kiterjesztése 2 Már megismert fogalmak áttekintése Objektumorientált

Részletesebben

és az instanceof operátor

és az instanceof operátor Java VIII. Az interfacei és az instanceof operátor Krizsán Zoltán Miskolci Egyetem Általános Informatikai Tanszék Utolsó módosítás: 2005. 10. 24. Java VIII.: Interface JAVA8 / 1 Az interfészről általában

Részletesebben

Access adatbázis elérése OLE DB-n keresztül

Access adatbázis elérése OLE DB-n keresztül Access adatbázis elérése OLE DB-n keresztül Készítsünk egy grafikus felülető alkalmazást, ami lehetıvé teszi egy Access adatbázisban tárolt hallgatói adatok (EHA, Név, e-mail cím) lekérdezését (összes

Részletesebben

Java VIII. Az interfacei. és az instanceof operátor. Az interfészről általában. Interfészek JAVA-ban. Krizsán Zoltán

Java VIII. Az interfacei. és az instanceof operátor. Az interfészről általában. Interfészek JAVA-ban. Krizsán Zoltán Java VIII. Az interfacei és az instanceof operátor Krizsán Zoltán Miskolci Egyetem Általános Informatikai Tanszék Utolsó módosítás: 2005. 10. 24. Java VIII.: Interface JAVA8 / 1 Az interfészről általában

Részletesebben

Java II. I A Java programozási nyelv alapelemei

Java II. I A Java programozási nyelv alapelemei Java II. I A Java programozási nyelv alapelemei Miskolci Egyetem Általános Informatikai Tanszék Utolsó módosítás: 2008. 02. 19. Java II.: Alapelemek JAVA2 / 1 A Java formalizmusa A C, illetve az annak

Részletesebben

Java programozási nyelv 6. rész Java a gyakorlatban

Java programozási nyelv 6. rész Java a gyakorlatban Java programozási nyelv 6. rész Java a gyakorlatban Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2004. október A Java programozási nyelv Soós Sándor 1/16 Tartalomjegyzék

Részletesebben

Vizuális programozás gyakorlat

Vizuális programozás gyakorlat Vizuális programozás gyakorlat Képnézegető alkalmazás WPF alapú felülettel Készítsen egy WPF képnézegető alkalmazást, ami a mellékelt ábrának megfelelően a bal oldali oszlopban (Grid) egy könyvtárban található

Részletesebben

3. Osztályok II. Programozás II

3. Osztályok II. Programozás II 3. Osztályok II. Programozás II Bevezető feladat Írj egy Nevsor osztályt, amely legfeljebb adott mennyiségű nevet képes eltárolni. A maximálisan tárolható nevek számát a konstruktorban adjuk meg. Az osztályt

Részletesebben

.Net adatstruktúrák. Készítette: Major Péter

.Net adatstruktúrák. Készítette: Major Péter .Net adatstruktúrák Készítette: Major Péter Adatstruktúrák általában A.Net-ben számos nyelvvel ellentétben nem kell bajlódnunk a változó hosszúságú tömbök, listák, sorok stb. implementálásával, mert ezek

Részletesebben

Entity Framework alapú adatbáziselérés 2

Entity Framework alapú adatbáziselérés 2 Entity Framework alapú adatbáziselérés 2 Dr. Johanyák Zsolt Csaba http://johanyak.hu A gyakorlat célja az, hogy a korábban létrehozott Telefonszám kezelő alkalmazást kiegészítsük egy WPF típusú felülettel.

Részletesebben

Johanyák Zsolt Csaba: Ugráló gomb oktatási segédlet Copyright 2008 Johanyák Zsolt Csaba

Johanyák Zsolt Csaba: Ugráló gomb oktatási segédlet    Copyright 2008 Johanyák Zsolt Csaba Ugráló gomb Készítsünk egy egyszerű játékprogramot, ami egy mozgó nyomógombot tartalmaz. A nyomógomb beállított ideig marad egy helyben, majd az ablakon számára elhatárolt terület (panel) egy véletlenszerűen

Részletesebben

Pelda öröklődésre: import java.io.*; import java.text.*; import java.util.*; import extra.*;

Pelda öröklődésre: import java.io.*; import java.text.*; import java.util.*; import extra.*; Java osztály készítése, adattagok, és metódusok, láthatóság, konstruktor, destruktor. Objektum létrehozása, használata, öröklés. ( Előfeltétel 12. Tétel ) Az osztály egy olyan típus leíró struktúra, amely

Részletesebben

C++ programozási nyelv

C++ programozási nyelv C++ programozási nyelv Gyakorlat - 8. hét Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2004. november A C++ programozási nyelv Soós Sándor 1/12 Tartalomjegyzék Miért

Részletesebben

Entity Framework alapú adatbáziselérés

Entity Framework alapú adatbáziselérés Entity Framework alapú adatbáziselérés Dr. Johanyák Zsolt Csaba http://johanyak.hu A gyakorlat célja Model-first megközelítéssel Entity-Framework modell létrehozása, majd ebből adatbázis generálása LocalDB-ben.

Részletesebben

C++ programozási nyelv

C++ programozási nyelv C++ programozási nyelv Gyakorlat - 13. hét Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2004. december A C++ programozási nyelv Soós Sándor 1/10 Tartalomjegyzék Objektumok

Részletesebben

OOP és UML Áttekintés

OOP és UML Áttekintés OOP és UML Áttekintés Tóth Zsolt Miskolci Egyetem 2013 Tóth Zsolt (Miskolci Egyetem) OOP és UML Áttekintés 2013 1 / 32 Tartalom jegyzék 1 OOP Osztály Öröklődés Interfész, Absztrakt Osztály Kivétel kezelés

Részletesebben

HORVÁTH ZSÓFIA 1. Beadandó feladat (HOZSAAI.ELTE) ápr 7. 8-as csoport

HORVÁTH ZSÓFIA 1. Beadandó feladat (HOZSAAI.ELTE) ápr 7. 8-as csoport 10-es Keressünk egy egész számokat tartalmazó négyzetes mátrixban olyan oszlopot, ahol a főátló alatti elemek mind nullák! Megolda si terv: Specifika cio : A = (mat: Z n m,ind: N, l: L) Ef =(mat = mat`)

Részletesebben

Globális operátor overloading

Globális operátor overloading Programozás II. 9. gyakorlat Operátor overloading 2: Unáris operátorok, globálisan megvalósított operátorok, > operátorok Kivételkezelés, IO library Globális operátor overloading Előző alkalommal

Részletesebben

Helyes-e az alábbi kódrészlet? int i = 1; i = i * 3 + 1; int j; j = i + 1; Nem. Igen. Hányféleképpen lehet Javaban megjegyzést írni?

Helyes-e az alábbi kódrészlet? int i = 1; i = i * 3 + 1; int j; j = i + 1; Nem. Igen. Hányféleképpen lehet Javaban megjegyzést írni? A "java Villa -v" parancs jelentése: A java interpreter elindítja a Villa osztály statikus main metódusát, és átadja neki paraméterként a "-v" stringet. A java interpreter elindítja először a Villa osztály

Részletesebben

Java programozási nyelv

Java programozási nyelv Java programozási nyelv 2. rész Vezérlő szerkezetek Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2005. szeptember A Java programozási nyelv Soós Sándor 1/23 Tartalomjegyzék

Részletesebben

Java és web programozás

Java és web programozás Budapesti M szaki Egyetem 2013. szeptember 25. 3. El adás User public class User { private String realname_; private String nickname_; private String password_; public User(String realname, String nickname)

Részletesebben

BME MOGI Gépészeti informatika 4.

BME MOGI Gépészeti informatika 4. BME MOGI Gépészeti informatika 4. 1. feladat önálló feladatmegoldás Generáljon két 1 és 10 közötti véletlen egész számot, majd kiírja ezekre a számokra a tízes szorzótáblákat! Ha az első generált szám

Részletesebben

Előszó... 13. 1. A Windows alkalmazásfejlesztés rövid története... 15. A Windows életútja... 15 A Windows 8 paradigmaváltása... 16

Előszó... 13. 1. A Windows alkalmazásfejlesztés rövid története... 15. A Windows életútja... 15 A Windows 8 paradigmaváltása... 16 Előszó... 13 1. A Windows alkalmazásfejlesztés rövid története... 15 A Windows életútja... 15 A Windows 8 paradigmaváltása... 16 A Microsoft megteszi az első lépéseket a fogyasztók felé... 17 A Windows

Részletesebben

C++ programozási nyelv Konstruktorok-destruktorok

C++ programozási nyelv Konstruktorok-destruktorok C++ programozási nyelv Konstruktorok-destruktorok Nyugat-Magyarországi Egyetem Faipari Mérnöki Kar Informatikai Intézet Soós Sándor 2004. szeptember A C++ programozási nyelv Soós Sándor 1/20 Tartalomjegyzék

Részletesebben

Entity Framework alapú adatbáziselérés

Entity Framework alapú adatbáziselérés Entity Framework alapú adatbáziselérés Dr. Johanyák Zsolt Csaba http://johanyak.hu A gyakorlat célja Model-first megközelítéssel Entity-Framework modell létrehozása, majd ebből adatbázis generálása LocalDB-ben.

Részletesebben

Függőség injekció Konstantinusz Kft 2010

Függőség injekció Konstantinusz Kft 2010 Függőség injekció Konstantinusz Kft 2010 1 Tartalomjegyzék 1 Tartalomjegyzék 2 2 Bevezetés 3 3 Függőségek formái 4 4 Függőség kezelés problémái 8 5 Megvalósítás 9 2/16 2 Bevezetés Egy objektum modellben

Részletesebben

Programozási alapismeretek 4.

Programozási alapismeretek 4. Programozási alapismeretek 4. Obejktum-Orientált Programozás Kis Balázs Bevezetés I. Az OO programozási szemlélet, egy merőben más szemlélet, az összes előző szemlélettel (strukturális, moduláris, stb.)

Részletesebben

OOP: Java 8.Gy: Abstract osztályok, interfészek

OOP: Java 8.Gy: Abstract osztályok, interfészek OOP: Java 8.Gy: Abstract osztályok, interfészek 26/1 B ITv: MAN 2019.04.03 Abszrakt metódus és absztrakt osztály. Gyakran előfordul a tervezés során, hogy egy osztály szintjén tudjuk, hogy valamilyen metódus

Részletesebben

Bánsághi Anna anna.bansaghi@mamikon.net

Bánsághi Anna anna.bansaghi@mamikon.net ESEMÉNYVEZÉRELT PROGRAMOZÁS Bánsághi Anna anna.bansaghi@mamikon.net 2. ELŐADÁS - C# ÁTTEKINTÉS - 2 2015 Bánsághi Anna 1 of 64 TEMATIKA I. C# ÁTTEKINTÉS II. WPF III. Modern UI 2015 Bánsághi Anna 2 of 64

Részletesebben

List<String> l1 = new ArrayList<String>(); List<Object> l2 = l1; // error

List<String> l1 = new ArrayList<String>(); List<Object> l2 = l1; // error Generics Egyszerűbb példák (java.util csomagból): public interface List { void add(e x); Iterator iterator(); public interface Iterator { E next(); boolean hasnext(); E - formális típusparaméter,

Részletesebben

Programozási nyelvek Java

Programozási nyelvek Java Programozási nyelvek Java 11.gyakorlat Operációsrendszertől függő tulajdonságok PATH elválasztó Unix ":" Windows ";" final String PATH_SEPARATOR = File.pathSeparator; Ugyanaz, csak karakterkent final char

Részletesebben

GENERIKUS PROGRAMOZÁS Osztálysablonok, Általános felépítésű függvények, Függvénynevek túlterhelése és. Függvénysablonok

GENERIKUS PROGRAMOZÁS Osztálysablonok, Általános felépítésű függvények, Függvénynevek túlterhelése és. Függvénysablonok GENERIKUS PROGRAMOZÁS Osztálysablonok, Általános felépítésű függvények, Függvénynevek túlterhelése és Függvénysablonok Gyakorlatorientált szoftverfejlesztés C++ nyelven Visual Studio Community fejlesztőkörnyezetben

Részletesebben

Programozási nyelvek II. JAVA

Programozási nyelvek II. JAVA Programozási nyelvek II. JAVA 8. gyakorlat 2017. november 6-10. Általános tudnivalók A feladatmegoldás során fontos betartani az elnevezésekre és típusokra vonatkozó megszorításokat, illetve a szövegek

Részletesebben

6. fejezet: Ciklusok

6. fejezet: Ciklusok 6. fejezet: Ciklusok Mint a nyelvekben általában, itt is léteznek ciklusok. Az alapvető három ciklus-típus: elöltesztelő, hátultesztelő és számláló. Lássuk ezeket sorban! Elöltesztelő = while. A while

Részletesebben

JAVA PROGRAMOZÁS 2.ELŐADÁS

JAVA PROGRAMOZÁS 2.ELŐADÁS Dr. Pál László, Sapientia EMTE, Csíkszereda JAVA PROGRAMOZÁS 2.ELŐADÁS 2014-2015 tavasz Tömbök, osztályok, objektumok, konstruktorok Tömbök 2 Referencia típusú változó Elemtípus Primitív Referencia: osztály,

Részletesebben

1. Alapok. #!/bin/bash

1. Alapok. #!/bin/bash 1. oldal 1.1. A programfájlok szerkezete 1. Alapok A bash programok tulajnképpen egyszerű szöveges fájlok, amelyeket bármely szövegszerkesztő programmal megírhatunk. Alapvetően ugyanazokat a at használhatjuk

Részletesebben

// keressük meg a legnagyobb faktoriális értéket, ami kisebb, // mint százmillió

// keressük meg a legnagyobb faktoriális értéket, ami kisebb, // mint százmillió BME MOGI Gépészeti informatika 3. 1. feladat Végezze el a következő feladatokat! Kérjen be számokat 0 végjelig, és határozza meg az átlagukat! A feladat megoldásához írja meg a következő metódusokat! a.

Részletesebben

Java Programozás 11. Ea: MVC modell

Java Programozás 11. Ea: MVC modell Java Programozás 11. Ea: MVC modell 20/1 B ITv: MAN 2018.03.02 MVC Model-View-Controller A modell-nézet-vezérlő a szoftvertervezésben használatos szerkezeti minta. Az MVC célja elválasztani az üzleti logikát

Részletesebben

OOP #14 (referencia-elv)

OOP #14 (referencia-elv) OOP #14 (referencia-elv) v1.0 2003.03.19. 21:22:00 Eszterházy Károly Főiskola Információtechnológia tsz. Hernyák Zoltán adj. e-mail: aroan@ektf.hu web: http://aries.ektf.hu/~aroan OOP OOP_14-1 - E jegyzet

Részletesebben

Programozási Paradigmák és Technikák

Programozási Paradigmák és Technikák Programozási Paradigmák és Technikák Eseménykezelés Névtelen metódusok (anonymous methods) V 1.0 OE-NIK HP 1 Eseménykezelés A prezentációban eseményt kiváltó és eseményt feldolgozó osztályról beszélünk,

Részletesebben

Programozás II. 2. Dr. Iványi Péter

Programozás II. 2. Dr. Iványi Péter Programozás II. 2. Dr. Iványi Péter 1 C++ Bjarne Stroustrup, Bell Laboratórium Első implementáció, 1983 Kezdetben csak precompiler volt C++ konstrukciót C-re fordította A kiterjesztés alapján ismerte fel:.cpp.cc.c

Részletesebben

Programozási nyelvek Java

Programozási nyelvek Java statikus programszerkezet Programozási nyelvek Java Kozsik Tamás előadása alapján Készítette: Nagy Krisztián 2. előadás csomag könyvtárak könyvtárak forrásfájlok bájtkódok (.java) (.class) primitív osztály

Részletesebben

Vizuá lis prográmozá s

Vizuá lis prográmozá s Vizuá lis prográmozá s Készítsen egy WPF alkalmazást, ami a hallgatok.mdf adatbázis állomány felhasználásával a következő feladatokat oldja meg: Kapcsolat nélküli adatbázis modell típusos DataSet segítségével.

Részletesebben

Adattípusok, vezérlési szerkezetek. Informatika Szabó Adrienn szeptember 14.

Adattípusok, vezérlési szerkezetek. Informatika Szabó Adrienn szeptember 14. Informatika 1 2011 Második előadás, vezérlési szerkezetek Szabó Adrienn 2011. szeptember 14. Tartalom Algoritmusok, vezérlési szerkezetek If - else: elágazás While ciklus For ciklus Egyszerű típusok Összetett

Részletesebben

RESIDENT EVIL CODENAME: NIK

RESIDENT EVIL CODENAME: NIK RESIDENT EVIL CODENAME: NIK Gyakorló zárthelyi dolgozat Figyelem! A feladat mennyiségre több anyagot tartalmaz, mint a zárthelyi dolgozat, amely az órán várható. Ennek oka, hogy több gyakorlásra legyen

Részletesebben

2011.11.29. JUnit. JUnit használata. IDE támogatás. Parancssori használat. Teszt készítése. Teszt készítése

2011.11.29. JUnit. JUnit használata. IDE támogatás. Parancssori használat. Teszt készítése. Teszt készítése Tartalom Integrált fejlesztés Java platformon JUnit JUnit használata Tesztelési technikák Demo 2 A specifikáció alapján teszteljük a program egyes részeit, klasszikus V-modell szerint Minden olyan metódust,

Részletesebben

OOP: Java 11.Gy: Enumok, beágyazott osztályok. 13/1 B ITv: MAN

OOP: Java 11.Gy: Enumok, beágyazott osztályok. 13/1 B ITv: MAN OOP: Java 11.Gy: Enumok, beágyazott osztályok 13/1 B ITv: MAN 2019.04.24 ArrayList Rugalmas tömb A tömbök korlátai Fix méret, nem lehet menet közben megnövelni Ha túl nagyra választjuk, fölösleges helyfoglalás

Részletesebben

Szkriptnyelvek. 1. UNIX shell

Szkriptnyelvek. 1. UNIX shell Szkriptnyelvek 1. UNIX shell Szkriptek futtatása Parancsértelmez ő shell script neve paraméterek shell script neve paraméterek Ebben az esetben a szkript tartalmazza a parancsértelmezőt: #!/bin/bash Szkriptek

Részletesebben

Vezérlési szerkezetek

Vezérlési szerkezetek Vezérlési szerkezetek Szelekciós ok: if, else, switch If Segítségével valamely ok végrehajtását valamely feltétel teljesülése esetén végezzük el. Az if segítségével valamely tevékenység () végrehajtását

Részletesebben

II. Mérés SZÉCHENYI ISTVÁN EGYETEM GYŐR TÁVKÖZLÉSI TANSZÉK

II. Mérés SZÉCHENYI ISTVÁN EGYETEM GYŐR TÁVKÖZLÉSI TANSZÉK Mérési Utasítás Linux/Unix jogosultságok és fájlok kezelése Linux fájlrendszerek és jogosultságok Linux alatt, az egyes fájlokhoz való hozzáférések szabályozása érdekében a fájlokhoz tulajdonost, csoportot

Részletesebben

Programozás I. 3. gyakorlat. Szegedi Tudományegyetem Természettudományi és Informatikai Kar

Programozás I. 3. gyakorlat. Szegedi Tudományegyetem Természettudományi és Informatikai Kar Programozás I. 3. gyakorlat Szegedi Tudományegyetem Természettudományi és Informatikai Kar Antal Gábor 1 Primitív típusok Típus neve Érték Alap érték Foglalt tár Intervallum byte Előjeles egész 0 8 bit

Részletesebben

Smalltalk 3. Osztályok létrehozása. Készítette: Szabó Éva

Smalltalk 3. Osztályok létrehozása. Készítette: Szabó Éva Smalltalk 3. Osztályok létrehozása Készítette: Szabó Éva Metaosztály fogalma Mint korában említettük, a Smalltalkban mindent objektumnak tekintünk. Még az osztályok is objektumok. De ha az osztály objektum,

Részletesebben

Johanyák Zsolt Csaba: Grafikus felület programozása. http://www.johanyak.hu e-mail: johanyak.csaba@gamf.kefo.hu Copyright 2008 Johanyák Zsolt Csaba

Johanyák Zsolt Csaba: Grafikus felület programozása. http://www.johanyak.hu e-mail: johanyak.csaba@gamf.kefo.hu Copyright 2008 Johanyák Zsolt Csaba Johanyák Zsolt Csaba: Grafikus felület programozása http://www.johanyak.hu e-mail: johanyak.csaba@gamf.kefo.hu Copyright 2008 Johanyák Zsolt Csaba 1. Gyümölcsárazó automata Készítsünk egy gyümölcsárazó

Részletesebben

Kézikönyv. Szelekciós jegyzék létrehozása

Kézikönyv. Szelekciós jegyzék létrehozása Kézikönyv Szelekciós jegyzék létrehozása Tartalomjegyzék 1 OBJEKTUM KIVÁLASZTÁS - VEVŐ MEGJELENÍTÉS... 4 2 VEVŐ - ÜRES... 6 3 ABAS-ERP MASZKINFÓ... 8 4 VEVŐ - ÜRES... 9 5 ABAS-ERP MASZKINFÓ... 11 6 VEVŐ

Részletesebben

Java gyakorlat feladatai e s megolda sai (2014.04.10)

Java gyakorlat feladatai e s megolda sai (2014.04.10) Java gyakorlat feladatai e s megolda sai (2014.04.10) 1. Feladat Számítsuk ki a Fibonacci sorozat első 20 tagját! / Fibonacci számsorozat tagjait kiszámoló, egyetlen osztályból álló program @author Bence

Részletesebben

C programozási nyelv

C programozási nyelv C programozási nyelv Előfeldolgozó utasítások Dr Schuster György 2011 május 3 Dr Schuster György () C programozási nyelv Előfeldolgozó utasítások 2011 május 3 1 / 15 A fordítás menete Dr Schuster György

Részletesebben

Programozási technikák Pál László. Sapientia EMTE, Csíkszereda, 2009/2010

Programozási technikák Pál László. Sapientia EMTE, Csíkszereda, 2009/2010 Programozási technikák Pál László Sapientia EMTE, Csíkszereda, 2009/2010 12. ELŐADÁS Adatbázis-kezelés Delphiben 2 Adatmegjelenítés lekérdezés segítségével A táblákhoz hasonlóan a lekérdezések is az adatbázis

Részletesebben

1. Egyszerű (primitív) típusok. 2. Referencia típusok

1. Egyszerű (primitív) típusok. 2. Referencia típusok II. A Java nyelv eszközei 1. Milyen eszközöket nyújt a Java a programozóknak Korábban már említettük, hogy a Java a C nyelvből alakult ki, ezért a C, C++ nyelvben járatos programozóknak nem fog nehézséget

Részletesebben

Eseményvezérelt és objektumorientált programozás

Eseményvezérelt és objektumorientált programozás DIALOG BOXES, DATA BINDING, STYLES, TRIGGERS WPF 1 Készítsük el a hallgatók és az oktatók nyilvántartását megvalósító modult. Mindkettő hasonló módon működik, ezért az alábbi leírásban csak a hallgatói

Részletesebben

Ugráló gomb oktatási segédlet Ugráló gomb

Ugráló gomb oktatási segédlet Ugráló gomb Ugráló gomb Készítsünk egy egyszerű játékprogramot, ami egy mozgó nyomógombot tartalmaz. A nyomógomb beállított ideig marad egy helyben, majd az ablakon számára elhatárolt terület (panel) egy véletlenszerűen

Részletesebben

A szerzõrõl... xi Bevezetés... xiii

A szerzõrõl... xi Bevezetés... xiii TARTALOMJEGYZÉK A szerzõrõl...................................................... xi Bevezetés...................................................... xiii I. rész A Visual Basic 2005 környezet 1. óra Irány

Részletesebben

Programozás C és C++ -ban

Programozás C és C++ -ban Programozás C és C++ -ban 2. További különbségek a C és C++ között 2.1 Igaz és hamis A C++ programozási nyelv a C-hez hasonlóan definiál néhány alap adattípust: char int float double Ugyanakkor egy új

Részletesebben

Abstract osztályok és interface-ek. 7-dik gyakorlat

Abstract osztályok és interface-ek. 7-dik gyakorlat Abstract osztályok és interface-ek 7-dik gyakorlat Abstract metódusok és osztályok Az OO fejlesztés során olyan osztályokat is kialakíthatunk, melyeket csak továbbfejlesztésre, származtatásra lehet használni,

Részletesebben

HVK Adminisztrátori használati útmutató

HVK Adminisztrátori használati útmutató HVK Adminisztrátori használati útmutató Tartalom felöltés, Hírek karbantartása A www.mvfportal.hu oldalon a bejelentkezést követően a rendszer a felhasználó jogosultsági besorolásának megfelelő nyitó oldalra

Részletesebben

Java II. I A Java programozási nyelv alapelemei

Java II. I A Java programozási nyelv alapelemei Java2 / 1 Java II. I A Java programozási nyelv alapelemei Miskolci Egyetem Általános Informatikai Tanszék Utolsó módosítás: 2009. 02. 09. Java II.: Alapelemek JAVA2 / 1 A Java formalizmusa A C, illetve

Részletesebben

Web-technológia PHP-vel

Web-technológia PHP-vel Web-technológia PHP-vel A PHP programnyelv 2, futtatókörnyezet beálĺıtások Erős Bence February 26, 2013 Erős Bence () Web-technológia PHP-vel February 26, 2013 1 / 19 Szuperglobális változók $ GET : request

Részletesebben

Programozás BMEKOKAA146. Dr. Bécsi Tamás 5. előadás

Programozás BMEKOKAA146. Dr. Bécsi Tamás 5. előadás Programozás BMEKOKAA146 Dr. Bécsi Tamás 5. előadás Tömbök átméretezése public static void Resize( ref T[] array, int newsize ) Példa: int[] a=new int[20]; Array.Resize(ref a, 22); 2016. 10. 19.

Részletesebben

BME MOGI Gépészeti informatika 1.

BME MOGI Gépészeti informatika 1. BME MOGI Gépészeti informatika 1. 1. feladat Végezze el a következő feladatokat! Olvassa be a nevét és írjon üdvözlő szöveget a képernyőre! Generáljon két 1-100 közötti egész számot, és írassa ki a hányadosukat

Részletesebben

Programozás BMEKOKAA146. Dr. Bécsi Tamás 1. Előadás

Programozás BMEKOKAA146. Dr. Bécsi Tamás 1. Előadás Programozás BMEKOKAA146 Dr. Bécsi Tamás 1. Előadás Bemutatkozás Előadó: Dr. Bécsi Tamás St.106, (1)463-1044, becsi.tamas@mail.bme.hu Közlekedés-, és Járműirányítási Tanszék www.kjit.bme.hu Programozás

Részletesebben

C# Nyelvi Elemei. Tóth Zsolt. Miskolci Egyetem. Tóth Zsolt (Miskolci Egyetem) C# Nyelvi Elemei / 18

C# Nyelvi Elemei. Tóth Zsolt. Miskolci Egyetem. Tóth Zsolt (Miskolci Egyetem) C# Nyelvi Elemei / 18 C# Nyelvi Elemei Tóth Zsolt Miskolci Egyetem 2013 Tóth Zsolt (Miskolci Egyetem) C# Nyelvi Elemei 2013 1 / 18 Tartalomjegyzék 1 Object 2 Típusok 3 String 4 RegEx Tóth Zsolt (Miskolci Egyetem) C# Nyelvi

Részletesebben

SZÁMÍTÓGÉPES PROBLÉMAMEGOLDÁS

SZÁMÍTÓGÉPES PROBLÉMAMEGOLDÁS SZÁMÍTÓGÉPES PROBLÉMAMEGOLDÁS 2.ELŐADÁS A VB programozási nyelv Az Excel programozása 2 A VB programozási nyelv Adattípusok Adatok kezelése Vezérlőszerkezetek Adattípusok és műveletek Egész adattípusok

Részletesebben

Programozási nyelvek Java

Programozási nyelvek Java Programozási nyelvek Java Kozsik Tamás előadása alapján Készítette: Nagy Krisztián 13. előadás Throwable Error Exception RuntimeException IOException Saját (általában) Nem ellenörzött kivételek (Unchecked

Részletesebben

Objektumorientált programozás C# nyelven

Objektumorientált programozás C# nyelven Objektumorientált programozás C# nyelven 2. rész Öröklés és többalakúság Nemvirtuális metódusok, elrejtés Virtuális metódusok, elrejtés Típuskényszerítés, az is és as operátorok Absztrakt osztályok, absztrakt

Részletesebben

POSZEIDON dokumentáció (1.2)

POSZEIDON dokumentáció (1.2) POSZEIDON dokumentáció (1.2) Bevezetés a Poszeidon rendszer használatába I. TELEPÍTÉS Poszeidon alkalmazás letölthető: www.sze.hu/poszeidon/poszeidon.exe Lépések: FUTTATÁS / (FUTTATÁS) / TOVÁBB / TOVÁBB

Részletesebben