A következő címkéjű bejegyzések mutatása: C#. Összes bejegyzés megjelenítése
A következő címkéjű bejegyzések mutatása: C#. Összes bejegyzés megjelenítése

2007-12-21

C# delegate-ek


Év vége előtti vincseszter-gyomtalanításnál találtam egy fájlot, amibe a C# delegate-jairól véstem fel pár gondolatot valamikor. Egyébként kicsit bánom hogy nem mentem a Javapolisra. Legközelebb -főleg ha ilyen lehetőség lesz- az asztalra csapok, félreteszem az aktuális (sz*rból kihúzandó) projektet és megcsinálom amit akarok.



A delegate a C-ben és C++-ban megszokott függvénypointerekre hasonlít legjobban.
Magának a szónak a jelentése: kiküldött követ, meghatalmazott, képviselő.



A delegate egy olyan adatstruktúra ami egy vagy több metódusra mutat, példánymetódusok esetében egyben az azokhoz tartozó objektumokra is. Mondhatnánk azt is, hogy a delegate, mint deklaráció egy metódus szignatúráját írja le. Azokkal a metódusokkal kompatibilis, amelyeknek a paraméterlistája és a visszatérési értéke megegyezik a delegate paraméterlistájával és visszatérési értékével.


Delegate-t így kell deklarálni:


delegate int op(int a, int b);


Ha osztályon kívül deklaráljuk akkor a namespace-n belül lehet elérni, de ha osztályon belül deklaráljuk (static kulcszó nem megengedett), akkor az osztály nevén keresztül kell hivatkozni rá. Ilyenkor tehát a delegate nem a példányhoz hanem az osztályhoz tartozik.


A delegate a System.Delegate osztályból származik, viszont ezt az öröklődést csak a rendszer és a compiler-ek használják, mert maga az osztály sealed, tehát a programozáskor mi csak a delegate kulcsszóval találkozunk.


Delegate példányosítása a következőképpen történik:


op x = new op(add);


Ahol az add metódusnak kompatibilisnak kell lennie a delegate-tel, különben fordítási hiba jelentkezik.


Egy delegate-hez újabb metódusokat is hozzá lehet adni:


x += new op(sub);

x = new op(add) + new op(sub);


stb. Ha új értéket rendelünk a delegate-hez, a régi elveszik, mint a referenciáknál megszokott.



A delegate meghívása egyszerűen így történik:


x(1, 2);


Ilyenkor az összes metódus végrehajtódik szépen sorban szinkronban ugyanazokkal a paraméterekkel. Ha egyik metódus változtat valamit, azt a változást a sorban utána következo metódusok mind látni fogják. A legutolsó visszatérési érték fogja képezni a delegate visszatérési értékét. Ha exception következik be a hívások során és az nincs elkapva a híváslistában meghívott metódusban, akkor ez az exception a delegate-et hívó kódban fog jelentkezni, mintha a delegate hívása során keletkezett volna. Az exception keletkezése után a híváslistában szereplő többi metódus nem kerül meghívásra.



A delegate-bol el is lehet venni metódusokat a - és a -= operátorral. Ha olyan metódust próbálunk elvenni belőle ami előzőleg nem volt benne, nem történik változás.


Delegate-ek összehasonlítása



A delegate-ekre alkalmazható az egyenlőséget ill. az egyenlőtlenséget vizsgáló összehasonlító operátor (==,!=). Két delegate példány egyenlőnek tekinthető, ha mindkettő értéke null, vagy egyenlő híváslistával bírnak, tehát a híváslistákban szereplő metódusok ugyanazokra a statikus metódusokra és ugyanazon objektumpéldányok ugyanazon metódusaira mutatnak ugyanabban a sorrendben a két delegate esetében. A delegate-ek típusa nem számít, tehát két különböző típusú delegate (amik persze ugyanazt a szignatúrát képviselik) lehet egyenlő.


Event-ek



A delegate int op(int a, int b) tehát egy delegate-nek a deklarációja és már látható volt, hogyan lehet lokális változóként létrehozni. (Miért van külön event, ezt nem értem...)


public event Click EventHandler;


Így néz ki tehát egy event deklarációja. Az event kulcsszó, aztán az esemény neve (érdemes valami igeként elnevezni) aztán a típusa. Így egy delegate típus könnyen hozzárendelhető többféle eseményhez.


A delegate-ek és event-ek kiválóan használhatóak eseménykezelésre, ahogy ez meg is van valósítva a System.EventHandler esetében. Egy gombhoz például a következőképpen rendelhetünk egy eseménykezelőt:


button.Click += new EventHandler(this.buttonClick);


Ahol a Click egy event amihez EventHandler típusú metódusokat lehet kapcsolni. Az osztályon belül így történik az eseménykezelő hívása:


Click(this, null);


A megfelelő paraméterekkel természetesen.


Java vs. .net



A java 1.4-gyel szemben gyakorlatilag beépítették a nyelvbe az eseménykezelést. A jávás megoldásnak több hátránya is van a C#-hoz képest:


  • Meg kell írni vagy örökölni kell valahonnan a listenereket kezelő metódusokat. (add, remove, notify) Az örökléssel az a probléma hogy kizárja a máshonnan való öröklést, a logika megírása pedig a plusz hibalehetőségekkel járó ujjgyakorlat. Elvileg lehet írni egyedi implementációkat (pl. Chain of Responsibility), de az alkalmazott listener minta nem egy olyan dolog amit túlzottan meg lehetne fűszerezni.

  • C#-ban meg lehet csinálni, hogy az eseménykezelő metódusok nem ugyanabban az osztályban foglalnak helyet. A java-ban a Listener interfészek csokorba gyűjtik a metódusokat, amiket egy adott osztálynak implementálnia kell. (Billentyűeseményeknél lehet hogy csak a leütésre akarok figyelni, de az alkalmazandó interfész miatt a felengedésre is meg kell csinálnom az üres metódust.) Erre be is vezették az Adapter-osztályokat, amikben eleve benne van az összes metódus üres törzzsel, de ezek szintén öröklődést igényelnek.

  • Kissé antipattern az a megoldás, hogy egy meglévő -valamilyen célra már felhasznált osztályt- hirtelen kiegészítünk valami Listener logikával, mert éppen kell. Pl. public class MyForm extends JPanel implements ActionListener. Így az osztályban definiált public void actionPerformed(ActionEvent) metódust boldog boldogtalan hívogathatja. C#-ban egyszerűen lehet csinálni olyan eseménykezelő metódusokat amelyek az adott osztályon kívülről nem hozzáférhetőek. Ehhez jávában befoglalt osztályok kellenek, amik persze hosszabb távon egész jól megszokhatóak.

  • Már meglévő osztályok metódusait is fel lehet használni, anélkül hogy bele kellene nyúlnunk a kódjukba és implementálnunk kellene mégegy interfészt. Ezt java-ban wrapper osztállyal lehet megvalósítani, ami delegálja a metódushívást.


A fentiek ellenére nem hiányolom a delegate-eket a java-ból, főleg a hozzáadásos, összehasonlításos dolgait. Gyakorlatilag kis kerülőúttal mindent meg lehet oldani delegate-ek nélkül, ezenkívül több kérdést felvet, mint amennyit megold.



Mire jó még?



Kb. ugyanarra mint a C-s, függvénypointerek, tehát callback függvények megjelölésére, függvénytáblák létrehozására, stb. Egy példa függvénytábla létrehozására és használatára:


public op[] operations = new operations[10];

...

op[0] = new op(add);

...

operations[0](2, 3);

2005-11-23

ASP.NET oldal életciklus

Még régebben utánanéztem egy ASPX oldal életciklusának és lefirkantottam. Íme:

"Bejön a HTTP kérés az IIS szerverre. Ez a HTTP pipeline-n (HttpRuntime, IHttpHandler, IHttpHandlerFactory, ProcessRequest()) keresztül eljut az oldalhoz. Minden .aspx URL-hez ugyanolyan nevű aspx tartozik. Amikor az URL első alkalommal meghívódik az aspx forrás lefordítódik és egy assembly készül belőle.

Az oldal ProcessRequest metódusa hívódik meg.

Felépíti az oldalon található kontrollok hierarchiáját.

Init esemény generálódik.

Feltölti a kontrollokat a viewstate-jeikkel.

LoadPostData hívódik meg azoknál a kontrolloknál amik implementálják a IPostBackDataHandler-t. Itt még NEM szabadna eseménykezelőt indítani, mivel a viewstate még nincs feltöltve! Ehelyett, ha a LoadPostData true-t ad vissza később meg fog hívódni a RaisePostdataChangedEvent metódus.

Load esemény generálódik. Ekkor már be vannak állítva a kontrolok a kliensoldali interakcióknak megfelelően.

Ha van ilyen, meghívódik az eseménykezelő ami miatt a postback keletkezett. Ez az a bizonyos RaisePostDataChangedEvent metódus.

PreRender esemény generálódik. Ide még lehet olyan logikákat rakni ami az eventkezelés után és a renderelés előtt szükséges.

Aztán rendering módba kerül, legenerálja a HTML kódot és elküldi a választ.
Unload esemény generálódik.

Ha dupla request keletkezik, pl. egy gomb kétszeri megnyomásával, akkor az szépen beáll a queue-ba és végrehajtódik mindkét kérés."

2005-08-09

C# dátumkezelés

A dátumokkal mindig van valami zűr, úgyhogy ha valamit megtudok ide beírom, és ha újra kell akkor itt megnézem.

A DateTime először is value típus úgyhogy nem lehet neki null-t beállítani. Ezt legegyszerűbben úgy szokták kiküszöbölni hogy DateTime.MinValue-t állítanak be neki ami 1970.01.01-es dátumnak felel meg. Nem kell mondanom hogy ennek mi a hátránya.

Dátumformázás C#-ban:


string s = "2005-03-28 06:50";
try {
//see DateTimeFormatInfo for patterns
DateTime d = DateTime.ParseExact(s, "yyyy-MM-dd HH:mm", null);
Console.WriteLine(d.ToString("yyyyMMddHHmm"));
} catch(FormatException ex) {
Console.WriteLine("wrong date format.");
}

A DateTime value típus tárolja ugye a dátumot. A DateTime.Now adja rögtön vissza az aktuális dátumot, a DateTime Ticks propertyje pedig 100 nanosecundom "pontossággal" az óraütések számát. (Értsd.: 1 tick = 100 nanosec.) Ha a ticket megszorzom 10000-rel, akkor jön ki a milliszekundom. Loggolásnál hasznos.

Ha a mai nap kezdetére vagyunk kíváncsiak, akkor hasznos a:
now.Subtract(now.TimeOfDay);

Update 2008.07.23: Cikk a softwareonline-on a témában.