Személyes eszközök
Keresés

 

A InfoWiki wikiből



Tartalomjegyzék

Callback függvények

A "callback" technológia eléggét távol áll az OOP lényegétől. Ez egy örökség, melyet a programozók a hagyományos programozási nyelvekben szokhattak meg először, s mely azt nagy hatékonysággal és rugalmassággal ruházta fel. Ezen megoldás annyira életképes, hogy az OOP világában is lehet, sőt gyakran kell is alkalmazni. Ennek oka, hogy be kell ismerjük - vannak olyan programozási problémák, melyek az OOP szemlélettel való megoldása nehézkesebb, mint a hagyományos megközelítés.

A 'callback' olyan függvényeket jelent, melyeknek a memóriacímét eltároljuk valamely változóba, és ezen változóban tárolt memóriacím segítéségével ezt a függvényt aktivizálni tudjuk. Ezen csavaros megoldásban benne rejlik azon lehetőség, hogy ezen változóban cserélhetjük a benne tárolt függvény memóriacímét, így könnyedén megoldható, hogy ugyanazon programkód más-más függvényekkel dolgozzon.

Vegyük például azt az esetet, amikor a programunknak egy nagy méretű file-t kell hálózatról letöltenie. A hálózatról a file darabokban (blokkok) érkezik. Amikor lejött egy blokk, mindíg kérni kell a következőt. Ha nem szakad meg a kapcsolat, akkor előbb-utóbb lejön az összes blokk. Ez egy rendkívül lassú folyamat is lehet, a file méretétől, és a kapcsolat sebességétől függően akár percekig, de órákig is eltarthat.

abstract class FileLetolto
{
public void File_Letoltese(string url)
{
   HalozatiKapcsolatMegnyitasa();
   int meret = FileMeretLekerdezeseATavoliGeprol();
   int letoltott = 0;
   while (letoltott<meret)
   {
      byte[] blokk = egyBlokkLetoltese();
      letoltott+=blokk.Length;
      egyBlokkKiirasaHattertarra( blokk );
      FolyamatJelzes();
   }
   HalozatiKapcsolatLezarasa();
}
 
abstract void FolyamatJelzes();
 
}

A fenti vázlatos kódban a ciklusban szerepel egy 'FolyamatJelzes()' függvényhívás. Ezen függvénynek az lenne a szerepe, hogy a letöltés közben a felhasználót tájékoztassa a letöltés állapotáról. Ebben az osztályban ezen függvény nincs kidolgozva (abstract). A konkrét felhasználási esetben dől az el, hogy milyen módon jelzi ki ez a program a folyamatot. Elképzelhető, hogy egy földgömböt forgat, vagy folyamatjelző csíkot húz, esetleg kijelzi hogy mekkora az adatátvitel sebessége, mikorra várható a befejeződés, de az is lehet, hogy egy pálmafát rajzol ki a képernyőre, és egy kis makimajom mászik fel a fára folyamatosan, ahogy halad előre a letöltés (ez utóbbi jellemző lehet egy gyerekjátékban).

A fenti megoldással több gond is van:

  • a fenti függvény abstract, ami at jelenti, hogy az osztályt továbbfejleszteni kötelező, és a 'FolyamatJelzes()' függvényt megírni, kidolgozni is kötelező, mielőtt használhatnánk. Ez azért nem túl szerencsés, mert a folyamatkijelzés nem képezi érdemi részét a fenti kódnak. Egy rövid file gyors letöltése esetén túl nagy hűhót kell csapni, holott az akár folyamatkijelzés nélkül is letölthető lenne.
  • másrészről elképzelhető, hogy elkészítjük ennek az osztálynak a továbbfejlesztését oly módon, hogy a 'FolyamatJelzes()' egy földgömböt jelenít meg, melyet minden egyes blokk letöltése után elfordítunk 5 fokkal. A fentiek értelmében ezt mint gyerekosztály kell kidolgoznunk. Ekkor ezen gyerekosztály hirtelen tele lesz grafikával foglalkozó kóddal (képek, elforgatás-számlálás, stb). Eközben ezen osztály lényege még mindíg a file letöltés lenne hálózatról, és nem a grafika.

Ráadásul tegyük fel, hogy van egy príma kis osztályunk már a földgömbforgatásra, de nem tudjuk összehozni a két osztályt egyetlen közös osztállyá, hiszen nem lehet egyszerre két ősünk.

Callback megközelítés

Fogalmazzuk át a fenti problémát: a 'FileLetolto' osztály 'File_Letoltese' függvény menet közben hajlandó egy 'void'-ot visszaadó, paramétereket nem váró függvényt rendszeresen aktivizálni. Hogy ezen függvény amúgy mit csinál - ez a file letöltést valójában "nem érdekli". A kérdés az, hogy miért kellene ennek a függvénynek ugyanezen osztály részének lennie?

Dolgozzunk ezzel a megközelítéssel: először rögzítsük a függvény tulajdonságait:

  • 'void' típussal tér vissza
  • nem vár semmilyen paramétert
delegate void fv_folyamatKijelzo();

A 'delegate' kulcsszóval függvénytípusokat írthatunk le. Ezen függvénytípusnak (függvénycsoport) a neve a fenti példában 'fv_folyamatKijelzo', és a leírás szerint void-ot ad vissza, és nincs paramétere.

A 'delegate' kulcsszóval új típust hozhatunk létre. Ezen típusnévvel deklarálhatunk változókat, mezőket, paramétereket. Egy ilyen változó egy, a fenti szignatúrának megfelelő típusú függvény memóriacímét képes tárolni, de tárolhat cím hiányát jelző 'null' értéket is. Egy ilyen változó memóriaigénye 4 byte.

public void File_Letoltese(string url, fv_folyamatKijelzo userFv)
{
   HalozatiKapcsolatMegnyitasa();
   int meret = FileMeretLekerdezeseATavoliGeprol();
   int letoltott = 0;
   while (letoltott<meret)
   {
      byte[] blokk = egyBlokkLetoltese();
      letoltott+=blokk.Length;
      egyBlokkKiirasaHattertarra( blokk );
      // !!!!
      userFv();
      // !!!!
   }
   HalozatiKapcsolatLezarasa();
}

A fenti kódban az alábbi módosításokat végeztük el:

  • kiegészítettük még egy paraméterrel a függvényt ('userFv'), amely a fenti delegate

típusú. A 'delegate' típusleírás miatt tudjuk, hogy a benne lévő memóriacím egy függvényé, amely void-t ad vissza, és nem fog várni paramétert.

  • A ciklus belsejében ezen megkapott függvényt aktiváljuk a paraméterváltozó segítségével, felhasználva azt, hogy nem kell neki paramétert átadni, és nem lesz visszatérési értéke sem.

A fenti kód értelme az, hogy bármilyen fenti tulajdonságú függvényt hajlandó a ciklusból aktivizálni (viszahívni), akit paraméterként átadtunk neki.

A kód aktivizálása az alábbi módon történhet meg:


class ForgoFoldGombos
{
   public void foldgombForogjon()
   {
     ...
   }
}
 
class FoProgram
{
   public static void Main()
   {
     ForgoFoldGombos m = new ForgoFoldGombos();
     FileLetolto fl = new FileLetolto();
     // !!!
     fv_folyamatKijelzo fv = new fv_folyamatKijelzo( m.foldgombForogjon );
     // !!!
     fl.File_Letoltese("http://valami.hu/doksi.pdf",fv);
   }
}

Készítettünk a file letöltő osztálytól teljesen független osztályban egy 'foldgombForogjon' nevű függvényt. Ez (nem véletlenül) void-ot ad vissza, és nem vár paramétert. A 'Main()' belsejében példányosítunk a földgömbös osztályból, és a file letöltő osztályból is (ugyanis mind a 'foldgombForgjon', mind a 'File_Letoltese' függvények példányszintűek, használatukhoz példányra van szükség.

A követkető lépésben egy 'fv' változót deklarálunk, melynek a típusa a 'delegate' során megadott típus. Ebbe a változóba a fenti szignatúrának megfelelő jellegű függvény memóriacímét lehet elhelyezni. Ezt meg is tesszük egy értékadás során. Ennek sajátságos formája van: a 'fv_folyamatKijelzo' típust úgy fogjuk fel, mintha egy osztály lenne, akiből példányosítani kell, a konstruktora paraméterként magát a tényleges függvényt fogadja. Ügyeljünk rá, hogy az ebben a sorban szereplő 'm.foldgombForogjon' rész csak a függvényt azonosítja, nem hívja azt meg, tehát nem kell kitenni a függvényhívó operátorokat (zárőjelpár), és nem kell felparaméterezni!

Ezzel a fura szintaktikával a C# 1.x tervezői a példányosítás ismerős formáját akarták megőrízni az értékadás során. De kiderült, hogy ez inkább értelmet zavaró, mint javító hatást fejtett ki, ezért a C# 2.x-től kezdve a fenti értékadást egyszerűbb, tisztább formában is leírhatjuk:

// C# 1.x
fv_folyamatKijelzo fv = new fv_folyamatKijelzo( m.foldgombForogjon );
 
// C# 2.x
fv_folyamatKijelzo fv = m.foldgombForogjon;

Természetesen megoldható, hogy ne rakjuk bele egy erre a célre készített változóba előre az értéket - és adjuk azt tovább a 'File_Letoltese' paraméterei felé, hanem írjuk ezt rögtön a paraméterlistára:


// C# 1.x
fl.File_Letoltese(
 "http://valami.hu/doksi.pdf", 
 new fv_folyamatKijelzo( m.foldgombForogjon )
 );
 
// C# 2.x
fl.File_Letoltese( "http://valami.hu/doksi.pdf", m.foldgombForogjon );

A null érték kezelése

Térjünk vissza kicsit az eredeti függvényre. A 'userFv' paraméter értéke lehet 'null' is, például az alábbi módon:


fl.File_Letoltese("http://valami.hu/doksi.pdf", null);


Ekkor a függvény belsejében baj van, hiszen a 'null' érték azt jelenti, hogy nem adtunk át neki visszahívható függvényt. Ezt szerencsére könnyen kivédhetjük:

public void File_Letoltese(string url, fv_folyamatKijelzo userFv)
{
   ...
   while (...)
   {
      ...
      if (userFv!=null) 
         userFv();
   }
   ...
}

A fenti ellenőrzésre a biztonság kedvéért van szükség, hiszen a típus jellege megengedi a null értéket a 'userFv' paraméterváltozóban.

Paraméteres, és visszatérési értéket adó callback függvények

Természetesen van arra mód, hogy nem csak 'void'-ot visszaadó, és paraméterket nem váró függvénnyel dolgozzunk, hanem ennél bonyolultabb esetekkel is:

delegate void fv_letoltesMutato(int letoltott, int max, int szmperc);

A fenti függvény 3 paraméteres, mert a korrekt kijelzéshez szeretné megkapni az eddig letöltött byte-ok számát, a maximálisan letöltendő byte-ok számát, és a letöltésre fordított századmásodpercek számát. Ennek fényében ugyanis ki tudja jelezni az átlagos letöltési sebességet, és tud jósolni a várható befejezési időre vonatkozóan is.

A fenti típusú függvényt értelemszerűen megfelelően felparaméterezve kell majd meghívni:

public void File_Letoltese(string url, fv_letoltesMutato userFv)
{
   ...
   DateTime start = DateTime.Now;
   while (...)
   {
      ...
      DateTime akt = DateTime.Now;
      TimeSpan kulonb = akt-start;
      int szmperc = (int)kulonb.TotalMilliseconds;
      if (userFv!=null) 
         userFv(letoltott, meret, szmperc);
   }
   ...
}


Osztály és példányszintű callback függvények

Amikor ilyen callback (visszahívható) függvényt írunk, az lehet nemcsak példányszintű, de osztályszintű is. Mindkettő egyforma gyors, egyforma hatékony, ezért hogy melyiket választjuk - az inkább magán a callback függvény kódján múlik.

public class myCallbackClass
{
   public static void kijelez(int toltott, int maxToltendo, int ido)
   {
     ...
   }
}
 
// ebben az esetben (C# 1.x)
fv_letoltesMutato fv = new fv_letoltesMutato( myCallbackClass.kijelez );
 
// ebben az esetben (C# 2.x)
fv_letoltesMutato fv = myCallbackClass.kijelez;

Vagyis osztályszintű callback függvény esetén a callback függvény azonosítása a szokásos módon 'osztalynev.metodusnev' formában történik. Újra csak felhívnám a figyelmet, hogy függvényt nem meghívni kell, hanem azonosítani, vagyis nem kell kitenni a paraméterlistáját, sem a gömbölyű zárójpárt.

Példányszintű callback függvény esetén hasonlóan kell eljárni:

public class sajatKijelzo
{
   public void kijelez(int toltott, int maxToltendo, int ido)
   {
     ...
   }
}
 
sajatKijelzo m = new sajatKijelzo();
 
// ebben az esetben (C# 1.x)
fv_letoltesMutato fv = new fv_letoltesMutato( m.kijelez );
 
// ebben az esetben (C# 2.x)
fv_letoltesMutato fv = m.kijelez;

Vagyis a különbség most csak annyi, hogy a callback függvény azonosításához először példányosításra van szükség, majd a példányon keresztül a metódus azonosítható.

Callback típusú mező

Nem csak callback típusú változót és paramétert lehet készíteni, hanem ilyen típusú mezőt is. Egyszerűen megoldható, hogy a szóban forgó 'File_Letoltese' a callback függvényt nem paraméterként kapja meg, hanem előre elhelyezve egy megfelelő mezőben:

class FileLetolto
{
// a mező
public fv_letoltesMutato userFv;
 
public void File_Letoltese(string url )
{
   ...
   while (...)
   {
      ...
      if (userFv!=null) 
         userFv(letoltott, meret, szmperc);
   }
   ...
}

A mező lehet értelemszerűen példányszintű, vagy osztályszintű. Érdemes a kódban felkészülni, hogy a mező értéke hátha 'null', ezt a callback aktiválása előtt érdemes egy egyszerű 'if'-el ellenőrízni.

Callback-lista kezelése ArrayList-el

Az előző probléma fejlesztése, hogy nem csak egy callback függvényt kell visszahívni, hanem akár többet is. Ekkor nem egy ilyen mezőre van szükség - sokkal inkább egy listára, amelyhez annyi callback függvényt adhatunk hozzá, amennyit akarunk. A C# 1.x-ben az ilyen jellegű feladathoz 'ArrayList'-t kell használni, mivel a Generic támogatás csak a C# 2.0-ban jelent meg:

class FileLetolto
{
// fv_letoltesMutato típusú elemekből készült lista
public ArrayList userFvLista = new ArrayList(); 
 
public void File_Letoltese(string url )
{
   ...
   while (...)
   {
      ...
      foreach(fv_letoltesMutato userFv in userFvLista)
         userFv(letoltott, meret, szmperc);
   }
   ...
}

A gond a fenti egyszerű megoldással, hogy a 'foreach' ciklus feltételezi, hogy az ArrayList-ben csak 'fv_letoltesMutato' típusú elemek lesznek. Amennyiben nem, úgy a foreach ciklus kivételt fog okozni, ami nem szerencsés. A típushelyességet az ArrayList sajnos nem garantálja, ha 'public' elérhetőségű. A 'foreach' ciklusban persze lehetne 'Object' típusú elemekkel működni, és utána ellenőrízni a típushelyességet, de ez tovább lassítaná a minden while cikluslefutás esetén a feldolgozást:

foreach(Object userFv in userFvLista)
if (userFv!=null && userFv is fv_letoltesMutato)
  (userFv as fv_letoltesMutato)(letoltott, meret, szmperc);

Helyette inkább javasolt módszer az ArrayList elrejtése (protected), és két művelet biztosítása a lista kezeléséhez:

  • "feliratkozás": egy új callback függvény hozzáadása a listához
  • "leiratkozás": egy már felírt callback függvény eltávolítása a listáról
class FileLetolto
{
// fv_letoltesMutato típusú elemekből készült lista
protected ArrayList userFvLista = new ArrayList(); 
 
public void feliratkozas(fv_letoltesMutato fv)
{
  if (fv==null) return;
  if (userFvLista.Contains(fv)) return;
  userFvLista.Add( fv );
}
 
public void leiratkozas(fv_letoltesMutato fv)
{
  userFvLista.Remove( fv );
}
}

A fenti módosítással garantált, hogy a lista nem tartalmaz 'null' elemeket, minden callback függvényt maximum egy példányban tartalmaz, és csakis megfelelő típusú elemeket tartalmaz. Ezért a függvények visszahívása újra biztonságos:

foreach(fv_letoltesMutato userFv in userFvLista)
userFv(letoltott, meret, szmperc);

Ezen callback-lista akár arra is alkalmas, hogy a while cikluslépésben megkapott (beolvasott) adatokat feldolgozza. A függvények 'bool' típussal is visszatérhetnek, jelezvén hogy sikeresen feldolgozták a kapott adatokat. Ilyen megoldást lehet használni akkor, ha a kapott adatok valamilyen kódolt, vagy titkosított módon érkeznek, a listában pedig olyan callback függvények vannak, amelyek különböző dekódoló algoritmusokat tartalmaznak. Az a függvény fog visszatérni 'true' értékkel, amelyik dekódolni tudta az adatokat. Ekkor a maradék függvényeket a listáról már nem kell meghívni. A fenti listával ezt könnyű kezelni:

foreach(fv_letoltesMutato userFv in userFvLista)
if (userFv(letoltott, meret, szmperc)) 
  break;

Callback-lista kezelése event-el

A fenti, callback függvény-listára a C# beépítetten is tartalmaz megoldást. A neve 'event'. Az event gyakorlatilag függvények listáját manageli, de típushelyesen:

delegate void fv_letoltesMutato(int letoltott, int max, int szmperc);
 
class FileLetolto
{
    public event fv_letoltesMutato userFvListaEvent; 
 
    public void File_Letoltese(string url )
    {
       ...
       while (...)
       {
          ...
          if (userFvListaEvent!=null) 
             userFvListaEvent(letoltott, meret, szmperc);
       }
       ...
    }
}

A fenti kódban a 'delegate' után egy olyan példányszintű mezőt hozunk létre, amely 'fv_letoltesMutato' alaptípusú 'event', vagyis nem csak egyetlen ilyen callback függvényt képes tárolni, hanem akár többet is. Amennyiben ezen mező értéke 'null', úgy az event lista üres. Ezt sajnos le kell ellenőrízni egy 'if'-el, mivel elképzelhető az is, hogy egyetlen callback függvény sem iratkozott fel. Az 'ArrayList'-es megoldásban ezzel nem kellett különösebben törődni, hiszen az ArrayList mező attól, hogy maga a lista üres, nem null értékű.

Ugyanakkor vegyük észre, hogy az event listára feliratkozott callback függvények visszahívásához nem szükséges foreach ciklus, az 'userFvListaEvent(letoltott, meret, szmperc)' kódsor egy lépésben végzi az összes függvény visszahívását. Az "egy lépésben" azt jelenti, hogy a callback függvények egymás után, sorban hívódnak meg - de ehhez ez az egyetlen kódsor elég. A többit a futtató rendszer végzi majd el.

Megjegyeznénk, hogy az event alapú megközelítés esetén a callback függvények jellemzően void típusú visszatérési értékkel rendelkeznek, hiszen az egyes feliratkozott függvények visszatérési értékeit a hívás helyén nem kapjuk meg, s e miatt nem is tudjuk azt fogadni és feldolgozni.

Az event-re feliratkozni a '+=', leiratkozni a '-=' operátorokkal lehet:


FileLetolto f = new FileLetolto();
 
// ebben az esetben (C# 1.x)
f.userFvListaEvent += new fv_letoltesMutato( m.kijelez );
f.userFvListaEvent -= new fv_letoltesMutato( m.kijelez );
 
// ebben az esetben (C# 2.x)
f.userFvListaEvent +=  m.kijelez;
f.userFvListaEvent -=  m.kijelez;

Callback-lista kezelése generic listával (C# 2.0)

A C# 2.0 verziótól kezdve a generic típusú lista technológia is rendelkezésre áll. Ennek megfelelően az ArrayList-et lecserélhetjük egy típusbiztos listára. A módosítások az alábbiak:

class FileLetolto
{
    // típusú elemekből készült lista
    public List<fv_letoltesMutato> userFvLista = new List<fv_letoltesMutato>();
}

A hagyományos ArrayList helyett itt a 'List<fv_letoltesMutato>' generic típust használjuk. Ez eleve biztosítja, hogy a listában csakis ilyen típusú callback függvények kerülhetnek, így a 'protected' védelmi szintet 'public'-ra módosíthatjuk, és nincs szükség a 'feliratkozas' és 'leiratkozas' metódusokra, helyettük direktben hívható a lista '.Add()' és '.Remove()' metódusai erre a célra.

TIE osztályok

A "TIE" angol szó magyarul "nyakkendő"-re (vagy "kötelék"-re) fordítható. Leghíresebb előfordulási helye a Csillagok Háborúja c. filmekben volt, ahol a vadászgépeket "tie vadászok"-nak hívták. A vadászgépek alakja egy csokornyakkendőre emlékeztetett (és kötelékben repültek - de valójában a TIE itt a Twin Ion Engine rövidítése (by Luc)).

A tie osztályok a szokásos class-ok, ám metódusok helyett ilyen callback típusú mezőket tartalmaznak. Ennek megfelelően nem rendelkeznek önálló funkcionalitással. A mezői feltölthetőek létező függvényekkel, amelyek akár különböző osztályokból "verődnek" össze. Ennek értelme, hogy a mezők feltöltését követően a példány teljes értékű osztálynak minősül, működik. Viszont a mezőkbe betöltött függvények akár menet közben is cserélhetőek, így a példány folyamatosan alkalmazkodhat a megváltozott körülményekhez.

Tegyük fel, hogy a tie példány callback függvényeket tartalmaz a mezőiben adatok mentésére és visszatöltésére. Normálisan, a program indulásakor ebbe olyan függvények vannak betöltve, amely az adatokat SQL szerverre töltik fel, és onnan olvassák vissza. Ugyanakkor a hálózat meghibásodása esetén a program futás közben leszakadhat az SQL szerverről. Hogy eközben se szünjön meg az adatok mentése, menet közben lecserélhetjük a callback függvényt egy olyanra, amelyik az adatokat lokális file-ba menti, amíg az SQL szerverrel a kapcsolat helyreáll. Ez a csere azonnal életbe lép. Amennyiben az eredeti példány referenciáját már több helyen is eltároltuk, ez a változás ezeken keresztül is azonnal életbe lép, mivel a példány mezőjében keletkezik a változás.

Jellemző alkalmazási területe a tie osztályoknak, a plugin alapú programok. A plugin-ek olyan DLL-ek, amelyek felépítése igazodik valamely előíráshoz. Például tartalmaz egy 'RegisterYourSelf' metódust, melyet a plugint betöltő "nagy" alkalmazás meghív. Ezen metódus az általa definiált callback függvényeket betölti egy ilyen tie osztály megfelelő mezőibe. Ezen keresztül fogja a kód többi része elérni a plugin szolgáltatásait a nélkül, hogy tudná, melyik pluginból is származnak azok.

Anonymus callback-ek (C# 2.0)

A C# 2.0 egyik újdonsága az anonymus (névtelen) callback függvények. Ezek olyan függvények, amelyek valójában nem elkülönítve helyezkednek el, nem alkotnak önálló függvényt, hanem egy függvény belsejében lévő kódrészlet:

delegate void Printer(string s);
 
static void Main()
{
 int szamlalo = 0;
 Printer p = new Printer(DoWork);
 p("Ezt ird ki");
 
 p = delegate(string j) 
 { 
    szamlalo ++;
    System.Console.WriteLine(j); 
 };
 
 p("No meg ezt is!");
}
 
static void DoWork(string k)
{
  System.Console.WriteLine(k);
}

A fenti kódban deklaráltuk a 'Printer' függvénytípust a 'delegate' segítségével. Van olyan függvényünk, amely megfelel ennek a szignatúrának: a 'DoWork' egy void-ot visszaadó, string típusú függvény.

A 'Main()' belsejében deklarálunk ilyen típusú változót, 'p'. Ezen változó ilyen szignatúrájú függvények memóriacímeit képes hordozni. Ezen 'p' változóba elhelyezhetjük a 'DoWork' függvény címét, és a 'p'-n keresztül meghívhatjuk azt.

Az anonymus delegate függvény törzse a 'Main()' függvény törzsében van beágyazva. Ezen kialakításán látszik, hogy bár önálló nevet nem adtunk neki, mégis megfelel a szignatúrának. Az önálló blokkot a 'delegate' kulcsszó vezeti be, és paramétere is van. Mint látjuk, az ilyen beágyazott függvényblokk egyébként hozzáférhet a 'Main()' függvény paramétereihez, de lokális változóihoz is ('szamlalo').

Az ilyen anonymus delegate függvények törzse általában rövid, valami kis apróság, mely kialakítható lenne önálló függvény formájában is, de egyszerűen a körítés (védelmi szint, nevet kitalálni neki, stb) túl fáradtságos munka lenne.

Ugyanakkor nem csak arra alkalmazható, hogy egy változóba elhelyezzük a címét, hanem még furcsább szintakszissal akár direktben, paraméterként is átadható:

delegate void fv_letoltesMutato(int letoltott, int max, int szmperc);
 
public void File_Letoltese(string url, fv_letoltesMutato userFv) { ... }
 
public void Akarmi()
{
  File_Letoltese(
    "http://doksi.hu/akarmi.pdf", 
    delegate (int letoltott, int max, int szmperc)
    {
      Console.WriteLine("{0}b, {1}b, {2}ms",letoltott, max, szmperc);
    }
  );
}

Itt a 'File_Letoltese' függvény hívásakor közvetlenül adjuk át az anonymus delegate kódot.

Hernyák Zoltán
A lap eredeti címe: „http://wiki.ektf.hu/wiki/Mp3/ea26
Nézetek
nincs sb_52.204.98.217 cikk