Zoltán's profileÁrvai Zoltán BlogjaBlogLists Tools Help

Blog


    September 26

    Silverlight 2.0 RC0 Publikus Release

    Ma reggel szép napra ébredtem! Aztán picit besötétült :)

    Na a jó hír, hogy kaptunk Silverlight 2.0-ból egy Release Candidate-et. És akkor a rossz hír: Only Developer Version :(

    Szóval plugin az nincs, ami viszont van az az Expression Blend 2 SP1 RC Preview, VS2008-hoz a szokásos Tools, amiben benne van az SDK, a runtime, a template, meg minden, ami szem szájnak ingere :) Na szóval továbbra is ott tartunk, hogy ez csak egy developer version, plugin nincs, ha valamit ezen a verzión fejlesztünk és kirakjuk a netre, akkor a felhasználó szája csak görbülni fog, mert plugin csak a beta 2-höz van továbbra is.

    Mielőtt sikongatva kirohannánk a szobából, hogy jajj de jó, akkor ez engem nem is érdekel, gondoljuk csak végig hogy mit is jelent ez... Egészen konkrétan azt, hogy a végleges silverlight release nem csak kicsit, hanem nagyon nem lesz kompatibilis a beta 2-es silverlight alkalmazásokkal (ez mondjuk várható volt). Ez az RC0 meg arra szolgál, hogy felkészüljünk rá. A cél, hogy egy tesztkörnyezetben elkészítsük a beta 2-es alkalmazásunk RC0-ás (amely, remélhetőleg ténylegesen kompatibilis kódot eredményez a végleges release-zel) változatát, hogy amint publikus a Silverlight 2 RTM, azonnal harcba küldhessük az RC0-ra upgradelt(??) beta 2-es alkalmazásunkat.

    Na szóval örömre nincs ok, megint lehet bújni a breaking changes listát, ami elég vaskosra sikerült. Van benne egy csomó változatás, amiről foggalmam sincs mi, vagy egy csomó, ami komoly korlátozás, van egy csomó, ami ésszerű változtatás, és van egy csomó hiányosság, amire nem látok a listában semmi hivatkozást. (Pl hogy a DependencyObject-ből nincs származatatás beta 2-ben és reméltem, az RTM-ben majd lesz...)

    Na szóval vegyes érzelmek kicsikét... Remélem használat közben majd átlátom a háttérben megbújt zsenialitást :)

    Na de hogy jókat is mondjak,  a vezérlők:

    Van ProgressBar!!!, vagy ComboBox!!!, van PasswordBox!!!, van MessageBox!!! meg új Skin templatek a vezérlőkhöz! Egyébként a vezérlők jó része, amiről már sokat hallhattunk (mert igen, készül még egy csomó, például TreeView, Expander, DockPanel, WrapPanel, AutoComplete, Accordion stb... ) nem lesznek benne a core-ban

    Viccet félretéve tessék megnézni a listát és tessék komolyan venni, nem véletlen ez az RC0, képet kaphatunk arról, hogy milyen lesz majd a final release. Én is jól nekiállok és összetúrom, hogy lássam mi a pálya :)

    July 03

    A Dictionary nemlétező sorosíthatósága

    Érdekes probléma vetődött fel a prog.hu társalgójában. Dictionary-t kellene xml sorosítani. Valljuk be az igény jogos. Szokásunkhoz híven püföljük is a billentyűzetet bőszen, aztán mikor futtatjuk azt kapjuk, hogy IDictonary-t nem lehet sorosítani. Lázasan turkálva az MSDN-t arcunkba csap a hideg levegő: "Nem volt idő implementálni". Két féle ember van. Az egyik itt leül és szidalmaz mindent és mindenkit akit ér. A másik, aki ezen a problémán könnyen átlibben (mert még emlékszik arra, hogy lehet programozni is a kattingatáson túl) és neki ár írni egy wrappert, vagy saját gyűjteményt. Nos én ez utóbbi csoportba tartozom.

    Először is tudni kell, hogy egy generikus Dictionary-ben KeyValuePair-ek vannak. Na most mivel az XML sorosító egy rakás követelményt támaszt az osztállyal szemben, aminek a KeyValuePair (úgy fest) nem tud megfelelni, ezért kell egy saját ilyen Entry osztály. Előkapjuk generikus típus ismereteinket és vmi hasonlót pötyöghetünk le.

    image

    Ugye miről van szó, két változóm van, egy Kulcs (T típusú) és egy Érték (U típusú). Kell bele egy default konstruktor, a mezők publikusak kell, hogy legyenek, akár csak az osztály, és két attribútumot is elhelyzeztem a változók elé, jelezvén, hogy az xml kimenetben attribútumként jelenjenek meg. A KeyValuePair-t kiváltottuk.

    Következő körben kéne maga az a saját gyüjtemény, vagy valami hasonló, ami eltárolja nekem ezeket az entry-ket. Most egy sima generikus belső listát hoztam e célból létre.

    image

    Úgy elsőre valami ilyesmit skiccelnék fel. Nyilván van ennél szebb megoldás is, de nekem ez most kellően egyszerű :)

    A kényelmesség kedvéért csináltam egy Add függvényt is, indexer most lemaradt. Minden add hívás, egy ilyen Entry példányt hoz létre és elhelyezi a belső listába, amihez egyébként az Items propertyn keresztül hozzá lehet férni. Egy-két attribútumot is elhelyeztem annak érdekében, hogy az XML kimenet egyszerű és rövid és áttekinthető legyen. Innetől kezdve egészen nyugodtan használhatjuk az új Dictionary-nket. Pl így:

    image

    Ebben a példában egy olyan MyDictonary példányt hozok létre, ahol a kulcs és az érték is string, utánna simán xml sorosítom. Nyilván lehet saját gyűjteményeket is írni, Interface-ekből építkezni, vagy teljesítményre helyezni a hangsúlyt, ha valami komolyabbat szeretnénk.

    Az Xml formázásnak köszönhető a végeredmény a következő:

    image

    January 17

    Bevezetés a C# 3.0 újdonságaiba(4.) - Bővítő függvények

    A Bevezetés a C# 3.0 újdonságaiba sorozat utolsó cikkében a bővítő függvényekről (extension methods) lesz szó. Ez volt számomra az az újdonság, melytől egy picit fáztam eleinte. Én megrögzött híve vagyok az OO világnak, és hogy őszinte legyek számomra a bővítő függvények kilógnak az "encapsulation" (egységbe zárás) szellemiségéből. Vagy mégsem?

    Tegyük fel, hogy van egy A osztályunk. Szeretnénk kibővíteni a képességeit azáltal, hogy megírunk egy-két plussz függvényt hozzá. Ha nem tudunk hozzáférni, az A osztály kódjához, mert mondjuk csak egy DLL-ben áll rendelkezésünkre, akkor a OO paradigma szerint a természetes lépés, hogy származtatunk belőle egy B osztályt, és odapakoljuk az új függvényeinket. Nade mi történik akkor. ha az osztályt a készítője ellátta a sealed kulcsszóval? Akkor bizony nincs származtatás! Nem hagytak nekünk más választást, mint szögre akasztani OO elveinket és kétségbe esésünkben definiálni egy statikus osztályt, amiben elhelyezzük a statikus függvényeinket, melyek paraméterként kaphatják az A osztály egy példányát. Hát érezzük, hogy ez minden csak nem az igazi. Nem kapcsolódik a függvényünk az osztályhoz, senki más nem fog tudni a függvényeinkről (nincs intellisense), stb... Pedig nem járunk messze a megoldástól, első pillantásra a bővítő függvények is valami ilyesmit csinálnak.

    Bővítő függvény alapok

    A bővítő függvények segítségével kiterjeszthetjük egy osztály képességeit, származtatás nélkül. Bővítő függvények készítésekor az első paraméter-t megelőzi a this kulcsszó, ezzel jelezvén, hogy az első paraméterként megjelölt típus nem más, mint az a típus, amit kiterjeszt! Nézzünk egy egyszerű példát erre.

    image

    Ez a bővítő függvény az object osztályt terjeszti ki. Meghíváskor egyetlen paramétert vár(ConsoleColor), majd ezzel a színnel kiírja az adott elem ToString() által visszaadott eredményét a konzolra. Szándékosan mondtam, hogy egy paramétert vár, ugyanis az első paraméter az az a típus, amit kiterjeszt, jelen esetben a object osztály.

    Nézzünk egy valamivel hasznosabb példát. Szeretnénk minden gyűjteményünkhöz egy konzolra kiirató metódust.

    image

    Azokat a típusokat tudjuk felsorolni, melyek implementálják az IEnumerable interface-t, így kézenfekvő, hogy bővítsük ki azt. This kulcsszó után jön a típus, majd a változónév. Ez a függvény, minden olyan objektumra meghívható, amely az IEnumerable-t megvalósítja, bejárja a gyűjteményt (list) és kiírja az elemeket a konzolra.  Elég egyszerű nem igaz?

    Felmerül a kérdés, hogy ez miben más, mint az eredeti elképzelésünk? Vessünk egy pillantást az alábbi kódrészletre, ahol meghívjuk a fenti bővítő metódust!

    image

    Úgy hívtam meg, mintha egy tag függvény lenne. Sőt, az intellisense fel is ajánlotta nekem! A WriteToConsole()-t meghívhattam volna egy int tömbre, egy arraylistre, egy string listára, stb...  Így már nem is haragszunk érte anniyra, nem igaz?

    Néhány fontos tényezőt szem előtt kell tartani, bővítő metódusok készítésekor:

    1. A bővítő metódusokat publikusnak és statikusnak kell deklarálni
    2. Csak statikus osztályban helyezhetünk ell bővítő metódusokat
    3. A bővítő metódusok csak a publikus adattagokhoz férnek hozzá

    A compiler fordításkor átnézi az összes statikus osztályt bővítő függvények után kutatatva, és fordítás időben elvégzi a szükséges kiterjesztéseket. Érték és referencia típusokat is egyaránt bővíthetünk. Az ajánlás szerint érdemes egy külön namespace-ben egy statikus osztályba pakolni az ilyen bővítő függvényeket, és inkább usingolni a namespace-t a projektünkben.

    Generikus bővítő függvények

    Érdemes egy kicsit külön tárgyalni a generikus bővítő függvényeket. A fenti példákban mindenhol meghatároztuk a típust, amit kiterjesztünk, így azt, hogy minél több típusra tudjuk alkalmazni, azzal váltottuk ki, hogy egy egyáltalános típust terjesztettünk ki, amiből származnak az egyéb specifikusabb típusok. (pl Objectből, IEnumerable-t valósítanak meg, stb...)

    Ennél azért lehetünk elegánsabbak és jóval gyorsabbak is, ha generikus szerkezetekben gondolkodunk. Például a fenti WriteToConsole függvényt átalakíthatjuk generikussá is.

    image

    Így a generikus IEnumerable<T> Interface-t bővítettük ki. Látható az alábbi ábrán, hogy az intellisense el is árul nekünk minden fontos információt a WriteToConsole függvényről.

      image

    Amit észre kell venni, hogy ezáltal nem egy típust bővítettünk ki, hanem azoknak egy halmazát! Ugyanis a bővítő metódusokat el kell készíteni IEnumerable<int>, IEnumerable<string>, IEnumerable<Books>, stb.. esetekre is. Ha az első WriteToConsoleWithColor függvényünket generikussá alakítanánk, és this object element helyett, this T element-et írnánk, akkor már is nem az object osztályt terjesztnénk ki, hanem egy tetszőleges típust (pl int, books, decimal, vagy akár object). (Az intellisense az írná nekünk, hogy (extension) void T.WriteToConsole<T>()

    Tagfüggvények Vs Bővítő függvények

    Jogosan merül fel a névütközés kérdése. Mi van akkor, ha van az A osztályunkban egy X függvény, és készítünk a B statikus osztályba egy X bővítő függvényt az A típushoz. Ha meghívjuk az A.X()-et, akkor mi fog lefutni az A osztály X tagfüggvénye, vagy a B osztály X bővítő függvénye? A válasz egyszerű, a tagfüggvény élvez elsőbbséget.

    A System.Linq névtérben rengeteg bővítő függvényt készítettek el nekünk, hiszen a Where, Select, Sum, Count, stb... egytől egyig ilyen bővítő függvények.

    A bővítő függvények használata biztonságos és kényelmes is. Fordítás idejű ellenőrzést kapunk használatukhoz, segítségükkel igen rugalmas megoldásokat készíthetünk. Azonban a kétkedés jogát fenntartom és azt tanácsolnám mindenkinek, hogy azért, mert ez a lehetőség megadatott, ne essünk neki mindennek és kezdjünk gőzerővel ilyen bővítő függvényeket gyártani. Akár hogy is, a bővítő függvények nagyszerű, ugyanakkor kicsit veszélyes eszközt is adtak a kezünkbe. Igyekezzünk továbbra is csínján bánni a használatukkal, próbáljuk megőrizni a kódunkban az átláthatóságot, a struktúráltságot és az egységbe zárást is próbáljuk szem előtt tartni, ugyanis a bővítő függvények kicsit talán kilógnak ebből az erős OO szemléletből.

    Remélem sokan hasznosnak találtátok ezt a bevezető jellegű cikksorozatot.

    January 10

    Bevezetés a C# 3.0 újdonságaiba(3.) - Lambda kifejezések

    Az egyik, talán első ránézésre legijesztőbb újdonság azok számára, akiknek a funkcionális programozás idegen, a C# 3.0-ban bevezetett lambda kifejezések. Segítségükkel kódot tudunk paraméterként átadni. Sokaknak biztos fel is sejlik a delegate-ek fogalma. Alapvetően egy sokkal kényelmesebb és érthetőbb szintaxis-t bocsátottak rendelkezésünkre, aminek segítségével névtelen függvényeket készíthetünk. Nézzük meg ugyanarra a feladatra, mindkét variációt:

    image

    Az első esetben a Where függvény paramétereként egy delegate-et(lényegében függvénypointert) adunk át és azt a kódot, amire mutat, rögtön definiáljuk is. Ugye ez a C# 2.0-ban bevezetett névtelen metódus. Ugye az történik, hogy minden listaelemre (adatbázisosan gondolkodva, "minden sorra") meghívjuk ezt a kódrészletet, ami akkor tér vissza igazzal, ha a szerző " John Steinbeck".

    A második esetben lambda kifejezést használtunk. Azt mondtuk, hogy csak azok a "b"-k (Book példányok) érdekelnek, melyekre igaz az, hogy az "Author"-juk "John Steinbeck". Tehát a szintaxis lényegében a következő: paraméterek => kifejezés

    Ami feltűnhet, hogy explicite nem adtuk meg "b" típusát. Ezt a fordító találta ki a Where bővítő metódus (későbbiekben részletesen) alapján. Természetesen explicite is meg lehet határozni a típust:

    image

    A fenti lambda kifejezés ún. predikátum, nem más, mint egy logikai kifejezés. Egy adott típusú paraméterre a visszatérési értek bool.

    A projekció olyan lambda kifejezés, mely egy adott típusú paramétertől eltérő típust ad vissza, és nem logikai kifejezés.

    image

    A fenti példa összegzi a könyvek árait. A Sum függvénynek lambda kifejezés segítségével meghatároztuk, hogy a b példány Price változója alapján végezze el az szummázást.

    A szokásos linq-s, sql-hez hasonló szintaxis, ilyen függvény hívásokba, a feltételek pedig lambda kifejezésekbe fordulnak. Sajnos ezt is nagyon jól kell ismernünk, ugyanis az szokásos sql-szerű szintaxisnak vannak korlátai, nem minden lehetőség érhető el benne.

    Az alábbi kódrészlet ugyanazt a lekérdezést valósítja meg, az egyik sql-szerű szintaxissal (query expression), a másik lambda kifejezésekkel és bővítő függvényekkel (extension methods).

    image

    A lambda kifejezéseket váltózóknak értékül adhatjuk felhasználva a Func delegate típust.

    image

    Előre deklaráltak nekünk jó pár ilyen Func delegate típust, hogy lambda kifejezéseink kellően rugalmasak lehessenek.

    A lambda kifejezések delegate-ként történő lefordítása és működése nagyon kényelmes, és kézenfekvő, ha memóriában lévő adathalmazokat szeretnénk lekérdezni (pl listák). De képzeljük el az a szituációt, amikor LINQ to SQL használatakor nagy adatbázisokból, nagy táblákból szeretnénk lekérdezni.

    Fejeltsük most el a Book osztályunkat, és képzeljük el, hogy van egy nagy adatbázisunk, és az egyik táblája a "Books" nevet viseli. Gondoljunk bele, hogy mi lenne, ha előbb betöltené memóriába az adatokat, és aztán kezdené a lekérdezéseket végrehajtani. Amit várunk az az, hogy a linq to sql a lambda kifejezéseinket fordítsa SQL lekérdezésekbe, majd azt küldje át az adatbázisnak, így csak az eredmény jusson el hozzánk. Ezt úgy érhetjük el, hogy ha Func típusú delegate-ek helyett kifejezés fákat (expression tree) alkalmazunk.

    image

    Látható, hogy nem sima Func-ként, hanem Expression<Func...>-ként deklaráltam a változót. Így nem készül IL kód belőle fordítás időben, csak futásidőben, így ki lehet értékelni, ahogy csak szükséges. Természetesen, ezt a LINQ to SQL elvégzi helyettünk. Az alábbi ábrán látható, hogy a Where bővítő metódus itt most már Expression<Func<Product,bool>> típusú predikátumot vár.

    image

    Fontos megjegyezni, hogy az Expression-ként deklarált kifejezést nem lehet közvetlenül futtatni, ellentétben a Func típusú delegate-ként deklarálttal.

    Nem szabad megijedni a lambdakifejezések használatától. Valójában egy nagyon jól használható, egyszerű dologról van szó, amit csak meg kell ismerni, rászánni egy órát és magunkévá tenni. Természetesen el lehet bonyolítani a végtelenségig, így akárcsak a többi újdonságot, ezt is használjuk óvatosan, hogy a kényelem ne menjen az olvashatóság, érthetőség kárára.

    December 31

    Bevezetés a C# 3.0 újdonságaiba (2.) - Object initializer-ek és anoním típusok

    A cél továbbra is a LINQ megismerése és elsajátítása, azonban ahhoz, hogy tudjuk használni ezt a kiváló technológiát, értenünk kell, hogy milyen egyéb nyelvi elemekre támaszkodik, hogyan működik. A linq működésének megértése elengedhetetlen követelmény, annak hatékony használatához.

    Object Initializers

    Ha C# 3.0-át megelőzően szerettünk volna egy osztályból egy példányt létrehozni, megfelelő paraméterekkel, akkor szükségünk volt egy megfelelően felparaméterezett konstruktorra. Ha a konstruktor nem volt képes minden számunkra szükséges tagváltozót beállítani, akkor kénytelenek voltunk property-k segítségével megtenni azt. Mostantól az object initializer-ek segítségével sokkal rugalmasabban tehetjük meg ugyanezt. Nézzük az alábbi osztálydefiníciót:

    image

    A fenti osztály konstruktora a PublishYear mezőt nem állítja be. Így ha azt be szerettük volna állítani, akkor azt egy külön hívással kellett eddig megtennünk.

    image

    Nézzünk néhány példát object initializer-t használva:

    image

    Az első példában az összes tagváltozónak értéket adtunk.  Tulajdonképpen a háttérben az object initializer implicit módon meghívta a default konstruktort, ezt követően beállította a mezők értékeit. Látható, hogy a szintaxis magáért beszél. A példányosítás során kapcsos zárójelek között kell megadni a mezőneveket, az értékadásokat, vesszővel felsorolva.

    A második példában látható, hogy csak két tagváltozót állítunk be, az "Author" mező üresen marad.

    A harmadik példában expliciten meghívjuk a nem default konstruktort. Pirospontért el lehet rajta gondolkodni, hogy mi történik akkor, ha a harmadik esetben nem csak a "PublishYear"-t, hanem a másik két tagot is beállítjuk, természetesen másik értékre, mint amit a konstruktornak adunk át.

    Az object initializerek segítségével csak publikus tagváltozók és property-k állíthatók be. A háttérben egy temporary változóba inicializál egy példányt, majd ha minden tagváltozót beállított, akkor a referenciát átadja a tényleges változónak. Ez a technika nagymértékben növeli a kód olvashatóságát és komoly rugalmasságot csempész bele. Természetesen az object initializer-ek egymásba ágyazhatók.

    image

    A fent látható osztálydefiníciókban kicsit furcsának tűnhetnek a property-k. Ezek az ún. auto-property-k. Nincs szükség nekünk privát változót létrehozni, majd hozzá a szokásos property-ket definiálni. A "prop Tab-Tab-ot" követően létrejövő sor mögött ott a teljes arzenál, ami a működéshez kell. A privát tag (amit nem látunk), a property get és set definíciója (amit szintén nem látunk) is automatikusan elkészül. Az alábbi kódrészlet egy rövidke példa a beágyazott object initializer-ek használatára:

    image

    A második tag egy osztálypéldány lesz, aminek tagváltozóit szintén object initializer segítségével állítottuk be. Ez eddig csupán egyfajta szintaktikai édesítőszer. Ahhoz, hogy meglássuk az igazi erősségét ennek az újdonságnak, meg kell ismernünk először az anoním típusok világát.

    Anonymous Types

    Létrehozhatunk ún. Anoním típusokat, melyek komoly szolgálatot tehetnek nekünk, például linq lekérdezések használatatkor.

    Íme egy egyszerű példa anoním típus használatára:

    image

    Látható, hogy a new kulcsszó után nem adtunk meg típusnevet, valamint object initializer segítségével adtuk meg és állítottuk be a tagváltozókat. A fenti definíció ekvivalens az alábbi osztálydefinícióval.

    image

    A var-ként deklarált anonymousType változó felveszi ezt az anoním típust. A compiler a következő típust készíti el nekünk. Igazi gyönyörűség! {Name = "<>f__AnonymousType0`2" FullName = "<>f__AnonymousType0`2[[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]"} Ebből az érdemi információ számunkra a típus neve: "<>f__AnonymousType0`2" , valamint a típus két tagváltozója, ami egy string, illetve egy int. Azaz nem kellett egy fölösleges segédosztályt definiálnunk, a compiler elkészítette nekünk, nem volt szükség fölösleges többlet munkára. Láthatjuk, hogy a var micsoda nagy segítséget nyújt számunkra. Nélküle ugyanis lehetetlen lenne az anoním típus használata. Hiszen milyen típusú változónak tudnánk értékül adni? (object már kasztolás) A var-ként definiált változó implicit módon felveszi ezt az anoním típust, és úgy használhatjuk, mint bármely hagyományos osztálypéldányt.

    Zárszóként nézzünk egy egyszerű linq-s példát arra, hogy miként tudjuk ezt kihasználni lekérdezésünkben.

    image

    A fenti lekérdezés az  1996 után publikált könyvek között szűr. Ami fel kell, hogy tűnjön, az az, hogy nem "select b" van odaírva, azaz a query változóba nem könyvek egy listája kerül be!!! Hanem select new {...} miatt anoním típusok egy listájával tér vissza a lekérdezés. Az anoním típusunknak két mezője van, az "Author" és a "Title". SQL-es gondolkodással a select b tulajdonképpen egy olyan osztály-t szimbolizál, ami egy teljes sort képvisel. Míg a fent említett projekció "oszlopokat" vág ki nekünk a sorból és egy anoním típusba csomagolja az eredményt. Fárasztó lenne írni minden ilyen projekcióhoz egy megfelelő csomagoló osztályt, nem igaz? Helyette anoním típusok és object initializer-ek segítségével egy roppant rugalmas és átlátható megoldást tudtunk elkészíteni. Természetesen használhattunk volna származtatott értékeket is. Az alábbi példán csak a select rész látható:

    image

    Az új anoním típusunk egyik mezője a könyv címe (Title), a másik a "Price" nevű kalkulált mező pedig a bruttó ára lesz.

    Lekérdezéseinknek innentől kezdve csak a képzelet, nomeg a józan ész szabhat határt. Remélem minden olvasó profitált vmit ebből a kis irományból. Végül pedig megragadnám az alkalmat és BOLDOG ÚJÉVET kívánnék minden kedves olvasónak!

    December 18

    WinForms - Hozzáférés control-okhoz külön szálból

    Bár nem mai technológia a WinForms, mégis sokak futnak abba a problémába, amikor külön szálból szeretnének hozzáférni az egyik control-hoz, programuk egy csúnya hibaüzenettel elszáll. Miért van ez?

    Ha a winforms-os controlunkat több szálból is próbálnánk elérni és mindenféle veszélyesebbnél veszélyesebb dolognak kitenni (ha nem, akkor is) előfordulhat, hogy inkonzisztens állapotba hoznánk, ami megengedhetetlen. A UI külön szálon fut, ezért a .NET Framework csúnyán ránkszól, ha egy másik szálból szeretnénk hozzáférni, ami nem fog menni. Egészen pontosan illegal cross-thread operation hibaüzenetet dob nekünk. Természetesen létezik megoldás a problémára, méghozzá több is, én most az egyik legegyszerűbbet szeretném itt bemutatni.

    Nézzük a problémát. Ez a kis program a gombnyomás hatására egy külön szálat indít, amely egy szöveges üzenetet ír bele a textBox-ba.

    image

    Munkánknak be is érik a gyümölcse, arra a sorra érve, ahol megpróbáljuk beállítani a Text property tartalmát a következő szeretetcsomagot kapjuk:

    image

    A probléma már nem ismeretlen számunkra, kérjünk hozzáférést a controlhoz. Egy lehetséges megoldás a következő: Hozzunk létre egy callback delegate-et, nézzük meg, hogy a textBox-unk InvokeRequired property-je true-e, és ha igen, akkor hívjuk meg a textBox Invoke függvényét a delegate-ünkkel, természetesen sajátmagunkat visszahívva. Ha false, akkor nyert ügyünk van, már írhatjuk is a textBox-ot.

    image

    Természetesen van más megoldás is Backgroundworker-ek használatával és egyéb varázslatokkal. De úgy gondolom a fenti megoldás magáért beszél.

    December 16

    LINQ - Bevezetés a C# 3.0 újdonságaiba (1.)

    A mai fejlesztői világot teljesen átitatja az objektum orientált gondolkodás, tervezés. Sajnálatos módon még ma is meg kell küzdenünk nem objektum orientált adatforrásokkal, mint például az XML, vagy a relációs adatbázisok. Ezt a kellemetlenséget igyekszik a linq elfedni előlünk. A Microsoft fejlesztői kitettek magukért, nyelvi szinten integrálták be ezt a rendkívül rugalmas és hatékony lekérdező nyelvet. Természetesen a gondolat nem újkeletű, gondoljuk csak az nQuery-re vagy az NHibernate-re. A fejlesztői világ reakciója természetesen nem egyhangú, vannak akik rögtön nekiálltak saját linq providerek fejlesztésének, (Linq 2 Sharepoint, Linq 2 Flickr, Linq 2 Google stb...). Hosszútávon mi is a cél? Gondoljunk bele, milyen kényelmes lenne, ha különböző adatforrásokhoz ugyanazt a lekérdező szintaktikát tudnánk alkalmazni, méghozzá C#-ból, vagy Visual Basic-ből. Mindegy, hogy egy SQL Servert vallatok, vagy egy xml fájlban keresek, netán egy Sharepoint listát dolgozok fel. (Aki írt már CAML-t az tudja, miről beszélek) De vannak olyanok is, akik szkepticizmussal teli pillantásokat vetnek rá, ugyanis a linq alaposan átírja az adathozzáférési rétegről(DAL) eddig alkotott elképzeléseinket.

    Node itt az ideje, hogy mások véleménye helyett, saját elképzelést tudjunk alkotni a LINQ-ról. Ezt a technológiát a .NET 3.5 hozta el számunkra, mégpedig C# 3.0-ba illetve VB 9.0-ba ágyazva. Itt most én a C# 3.0-át választottam.

    Az első, amit tudni kell, hogy egy linq lekérdezés bármely olyan adatforrásra rászabadítható, amely megvalósítja az IEnumerable<T> interface-t, azaz felsorolható. Az alábbi példában a PersonList tömbből azokat a stringeket válogatjuk ki, amelyeknek a hossza legalább 4. Ugye mennyire hasonlít az SQL-re? (Pirosponttért el lehet gondolkodni rajta, hogy vajon miért from - select a sorrend és miért nem select-from) Ezt követően a query elemeit egy mezei foreach-csel bejárhatjuk. Ezt a lekérdezés formát nevezik query expression-nek. Könnyen olvastható és érthető.

    image

    A fenti lekérdezést megírhattuk volna így is:

    image

    Ezt nevezzük method-based query-nek. A két lekérdezés teljesen egyértékű. Vizsgáljuk meg egy kicsit jobban ezt a második szerkezetet, ugyanis számos újdonságot rejt számunkra. Először is a PersonList objektum típusa string tömb! Nincs ilyen nevű tagfüggvénye, hogy Where, OrderBy vagy Select. Ezek az úgynevezett extension method-ok. "Később" definiált speciális statikus függvények, melyek kiterjesztik azon objektumok képességeit, melyekre alkalmazhatók. A fenti Where függvény egy extension method, melynek paramétere egy lambda expression. Ez nem más, mint egy egyszerű logikai kifejezés, ami a Where esetében azt mondja, hogy válaszd ki azokat a p-ket, melyekre igaz az, hogy a p hossza legalább 4. Első ránézésre talán kicsit elrettentő de valójában egyszerű dolgoról van szó. Amire érdemes figyelni, hogy ezekkel a nyelvi újdonságokkal, illetve a kód tömörségére törekedve hihetletlenül átláthatatlan kódokat lehet írni.

    Még egy dologra térnék ki anélkül, hogy túl mélyre próbálnék hatolni a C# 3.0 nyelvi újdonságaiban, ez pedig az implicit módon típusos lokális változó, azaz a "var". Sokan fáznak tőle, mert hogy típus nélküliség .net-ben nem szerencsés. Ők tévednek, mert nem erről van szó. A var-ként deklarált változó igenis nagyon erősen típusos, csak éppen implicit módon adom meg a típusát. Azaz, az lesz a típusa, ami az értékadás jobboldalán áll.

    image

    Nézzük meg az alábbi képet. A debug módban futtatott Locals ablakban látható, hogy "a" string, "b" int, "c" pedig DateTime típusú. A változó inicializálásakor implicit módon megadtuk a típusát. Hogy ezt miért jó? Sokszor egyszerűbb, kevesebbet kell gépelni, de a legfőbb indok, hogy ha anonymous type példányt hozunk létre, akkor értékül csak var-ként deklarált változónak tudjuk adni, hiszen nincs neve a típusnak.

    image

    Remélem senkit sem rettentettem el a linq-tól és a C# 3.0-ától, sőt éppen ellenkezőleg, szeretnétek jobban megismerni ezeket az újdonságokat, megérteni, hogy működnek és mire jók. Ez a cikksorozat ebben próbál segítséget nyújtani.

    December 14

    Péntek esti WPF mosoly

    Így pénteken este jobb dologom nem lévén Krisztián kollegám kérdését próbáltam megválaszolni. Ha netántán valaki olyan elvetemült lenne, hogy dinamikusan szeretne hozzáadni elemeket egy ListBox-hoz, és történetesen ezek az elemek stringek és mondjuk ugyanazok a stringek, akkor a listbox bizony megbolondul. Miért van ez, és mit lehet tenni ellene? Különböző stringeknél ez miért nem jelentkezik?

    Egészen konkrétan:

    image

    És akkor SingleSelect módban elkezdem klikkelgetni a listboxot, ami összevissza kiválaszt több elemet, annak ellenére, hogy single módban vagyunk, tiszta káosz az egész.

    Amit tudok, az a megoldás:

    clip_image001[6]

    Ha így adogatjuk hozzá az elemeket, akkor a dolog tökéletesen működik.

    Az Add függvény objecteket vár, tehát nem arról van szó, hogy csak ListBoxItem mehet bele. Az én gyanúm az, hogy a listbox az elemeit a Hashkódjuk alapján különböztetheti meg. Mivel a GetHashCode()-ot a stringek esetén felüldefiniálták oly módon, hogy ha két string tartalma megegyezik, akkor a hashcode-juk is egyezzen meg, így a listbox nem igazán tud mit kezdeni a dologgal. A második esetben a stringeket lényegében egy másik osztályba wrappeltük, konkrétan ListBoxItem-ekbe, így a különbség tétel nem okoz problémát. A dolog elég nagy mosolyt csalt az arcomra, és ez továbbra is csak tipp. Bárkinek szívesen fogadom bármi ötletét, esetleg vki jól reverse engineeringelje meg és akkor majd megtudjuk jól :)

    Szerk.

    A sejtés helyes. WinForms-ban a ListBox az add függvény hívása esetén egy ListBoxItem objektumot hoz létre, így a wrappelés megtörténik. Wpf esetén viszont megőrzi az eredeti típust, így sztringek esetén a hashcode összeakadhat. Special thnx to R1cs1 and Giorgio!