Megosztás a következőn keresztül:


System.Delegate és a delegate kulcsszó

Előző

Ez a cikk a meghatalmazottakat támogató .NET-osztályokról és arról, hogy ezek hogyan vannak megfeleltetve a delegate kulcsszónak.

Mik azok a meghatalmazottak?

Gondolj úgy a delegáltra, mint egy metódusra mutató hivatkozás tárolásának módjára, hasonlóan ahhoz, ahogyan egy objektumra mutató hivatkozást tárolsz. Ahogyan az objektumokat átadhatja a metódusoknak, a metódushivatkozásokat meghatalmazottak használatával is átadhatja. Ez akkor hasznos, ha rugalmas kódot szeretne írni, ahol különböző metódusok "csatlakoztathatók" a különböző viselkedések biztosításához.

Tegyük fel például, hogy van egy számológépe, amely két számon hajthat végre műveleteket. Ahelyett, hogy kemény kódolású összeadást, kivonást, szorzást és osztást használna külön metódusokra, delegáltak használatával bármilyen olyan műveletet képviselhet, amely két számot vesz fel, és eredményt ad vissza.

Delegálttípusok definiálása

Most lássuk, hogyan hozhat létre delegálttípusokat a delegate kulcsszó használatával. Delegálttípus definiálásakor lényegében egy sablont hoz létre, amely leírja, hogy milyen típusú metódusok tárolhatók az adott delegáltban.

Delegált típust metódusaláíráshoz hasonló szintaxissal definiálhat, de az elején a delegate kulcsszóval:

// Define a simple delegate that can point to methods taking two integers and returning an integer
public delegate int Calculator(int x, int y);

Ez a Calculator meghatalmazott bármely olyan metódusra hivatkozhat, amely két int paramétert használ, és visszaad egy int.

Lássunk egy gyakorlatiasabb példát. Ha rendezni szeretne egy listát, meg kell mondania a rendezési algoritmusnak, hogyan hasonlíthatja össze az elemeket. Lássuk, hogyan segítenek a meghatalmazottak a List.Sort() metódusban. Az első lépés egy delegálttípus létrehozása az összehasonlítási művelethez:

// From the .NET Core library
public delegate int Comparison<in T>(T left, T right);

Ez a Comparison<T> meghatalmazott bármilyen olyan metódusra hivatkozhat, amely:

  • Két típusú paramétert vesz igénybe T
  • int Egy értéket ad vissza, amely általában a következők közül kerül ki (-1, 0, vagy 1) a "kisebb mint", "egyenlő" vagy "nagyobb mint" viszony kifejezésére.

Ha egy ilyen delegált típust határoz meg, a fordító automatikusan létrehoz egy, az aláírásnak megfelelő osztálytSystem.Delegate. Ez az osztály kezeli a metódushivatkozások tárolásának és meghívásának összetettségét.

A Comparison delegált típusa egy általános típus, ami azt jelenti, hogy bármilyen típussal Thasználható. Az általános osztályokkal és metódusokkal kapcsolatos további információkért lásd: Általános osztályok és metódusok.

Figyelje meg, hogy bár a szintaxis hasonlít egy változó deklarálásához, valójában egy új típust deklarál. Delegálttípusokat definiálhat osztályokon belül, közvetlenül a névtereken belül, vagy akár a globális névtérben is.

Megjegyzés:

Nem ajánlott deklarálni a delegált típusokat (vagy más típusokat) közvetlenül a globális névtérben.

A fordító emellett hozzáadja és eltávolítja az ehhez az új típushoz tartozó kezelőket, hogy az osztály ügyfelei metódusokat vegyenek fel és távolítsanak el egy példány meghívási listájából. A fordító kötelező érvényűvé teszi, hogy a hozzáadott vagy eltávolított metódus szignatúrája megegyezzen a delegált típus deklarálásakor használt szignatúrával.

Meghatalmazottpéldányok deklarálása

A delegált típus megadása után létrehozhat ilyen típusú példányokat (változókat). Gondoljon erre úgy, mint egy "pont" létrehozására, ahol egy metódusra mutató hivatkozást tárolhat.

A C# változóihoz hasonlóan a delegált példányokat sem közvetlenül névtérben, sem globális névtérben nem deklarálhatja.

// Inside a class definition:
public Comparison<T> comparator;

Ennek a változónak a Comparison<T> típusa (a korábban definiált delegálttípus), a változó neve pedig comparator. Ezen a ponton comparator még nem mutat semmilyen metódusra – ez olyan, mint egy üres pont, amely a kitöltésre vár.

Deklarálhat deklarált változókat helyi változóként vagy metódusparaméterként is, ugyanúgy, mint bármely más változótípust.

Delegáltak meghívása

Miután rendelkezik egy metódusra mutató delegált példánnyal, meghívhatja ezt a metódust a delegálton keresztül. A delegált meghívási listájában szereplő metódusokat úgy hívhatja meg, hogy a delegáltat úgy hívja meg, mintha az egy metódus lenne.

A metódus az Sort() összehasonlítási delegált használatával határozza meg az objektumok sorrendjét:

int result = comparator(left, right);

Ebben a sorban a kód meghívja a meghatalmazotthoz csatolt metódust. A delegált változót úgy kezeli, mintha metódusnév lenne, és normál metódushívási szintaxissal hívhatja meg.

Ez a kódsor azonban nem biztonságos feltételezést tesz: feltételezi, hogy egy célmetódus lett hozzáadva a meghatalmazotthoz. Ha nincsenek metódusok csatolva, a fenti sor dobást okozna NullReferenceException . A probléma megoldásához használt minták kifinomultabbak, mint egy egyszerű null-ellenőrzés, és a sorozat későbbi részében szerepelnek.

Meghívási célok hozzárendelése, hozzáadása és eltávolítása

Most már tudja, hogyan definiálhat delegált típusokat, deklarálhat delegált példányokat és hívhat meg delegáltakat. De hogyan csatlakoztathat egy metódust egy meghatalmazotthoz? Itt jön képbe a delegált hozzárendelés.

Egy delegált használatához hozzá kell rendelnie egy metódust. A hozzárendelt metódusnak ugyanazzal az aláírással (ugyanazokkal a paraméterekkel és visszatérési típussal) kell rendelkeznie, mint amelyet a delegált típusa definiál.

Lássunk egy gyakorlati példát. Ha a sztringek listáját szeretné a hosszuk szerint rendezni. Létre kell hoznia egy összehasonlító módszert, amely megfelel a meghatalmazotti aláírásnak Comparison<string> :

private static int CompareLength(string left, string right) =>
    left.Length.CompareTo(right.Length);

Ez a metódus két sztringet használ, és egy egész számot ad vissza, amely azt jelzi, hogy melyik sztring "nagyobb" (ebben az esetben hosszabb). A módszer privátként van deklarálva, ami tökéletesen rendben van. Nincs szükség arra, hogy a metódus a nyilvános felület része legyen ahhoz, hogy meghatalmazottal használja.

Most már átadhatja ezt a metódust a List.Sort() metódusnak:

phrases.Sort(CompareLength);

Figyelje meg, hogy zárójelek nélkül használja a metódus nevét. Ez arra utasítja a fordítót, hogy konvertálja a metódushivatkozást egy később meghívható delegálttá. A Sort() metódus mindig meghívja az Ön CompareLength metódusát, amikor két sztringet kell összehasonlítania.

Explicitebb is lehet egy delegált változó deklarálásával és a metódus hozzá rendelésével:

Comparison<string> comparer = CompareLength;
phrases.Sort(comparer);

Mindkét megközelítés ugyanazt a dolgot valósítja meg. Az első megközelítés tömörebb, a második pedig explicitebbé teszi a delegált hozzárendelést.

Egyszerű metódusok esetén gyakori, hogy lambdakifejezéseket használunk külön metódus definiálása helyett:

Comparison<string> comparer = (left, right) => left.Length.CompareTo(right.Length);
phrases.Sort(comparer);

A Lambda-kifejezések tömör módot biztosítanak az egyszerű metódusok beágyazott definiálására. A lambda-kifejezések delegált célokhoz való használatát egy későbbi szakasz ismerteti részletesebben.

Az eddigi példák egyetlen célmetódusú delegáltakat mutatnak be. A delegált objektumok azonban támogathatják az olyan meghívási listákat, amelyek több célmetódust is csatolnak egyetlen delegált objektumhoz. Ez a képesség különösen hasznos eseménykezelési helyzetekben.

Delegált és CsoportosDelegált osztályok

A színfalak mögött a használt delegált funkciók a .NET-keretrendszer két kulcsfontosságú osztályára épülnek: Delegate és MulticastDelegate. Általában nem közvetlenül dolgozik ezekkel az osztályokkal, de ezek biztosítják a meghatalmazottak munkáját lehetővé tevő alapokat.

Az System.Delegate osztály és közvetlen alosztálya System.MulticastDelegate biztosítja a keretrendszer támogatását a meghatalmazottak létrehozásához, a metódusok delegált célokként való regisztrálásához és a meghatalmazottnál regisztrált összes metódus meghívásához.

Íme egy érdekes tervezési részlet: System.Delegate és System.MulticastDelegate maguk nem használható delegált típusok. Ehelyett az összes létrehozott delegálttípus alaposztályaként szolgálnak. A C#-nyelv megakadályozza, hogy közvetlenül örökölje ezeket az osztályokat – ehelyett a kulcsszót delegate kell használnia.

Amikor a delegate kulcsszóval delegálttípust deklarál, a C#-fordítóprogram automatikusan létrehoz egy MulticastDelegate osztályt az Ön specifikus aláírásával.

Miért ez a kialakítás?

Ez a kialakítás a C# és a .NET első kiadásában gyökerezik. A tervezőcsapatnak több célja is volt:

  1. Típusbiztonság: A csapat biztosítani akarta, hogy a nyelv megkövetelje a típusbiztonságot a meghatalmazottak használatakor. Ez azt jelenti, hogy a delegáltak helyes típussal és megfelelő számú argumentummal legyenek meghívva, valamint a visszatérési típusok fordításkor helyesen legyenek ellenőrizve.

  2. Teljesítmény: Ha a fordító konkrét delegálási osztályokat hoz létre, amelyek konkrét metódus-aláírásokat jelölnek, a futtatókörnyezet optimalizálhatja a delegálási hívásokat.

  3. Egyszerűség: A delegáltak szerepeltek a .NET 1.0-ás kiadásában, amely a generikusok bevezetése előtt volt. A tervezésnek az idő korlátain belül kellett működnie.

A megoldás az volt, hogy a fordító hozza létre a metódus-aláírásoknak megfelelő konkrét delegált osztályokat, biztosítva a típusbiztonságot, miközben elrejti a bonyolultságot.

Delegálási metódusok használata

Annak ellenére, hogy közvetlenül nem hozhat létre származtatott osztályokat, időnként az és az DelegateMulticastDelegate osztályokban definiált metódusokat is használhatja. Íme a legfontosabb tudnivalók:

Minden meghatalmazott, akivel dolgozik, a MulticastDelegate-ből származik. A "csoportos küldés" delegált azt jelenti, hogy egy meghatalmazotton keresztül történő híváskor több metóduscél is meghívható. Az eredeti kialakítás figyelembe vette, hogy különbséget tesz a meghatalmazottak között, amelyek csak egy metódust tudnak meghívni, szemben azokkal a meghatalmazottakéval, amelyek több metódust is meghívhatnak. A gyakorlatban ez a különbség kevésbé bizonyult hasznosnak, mint eredetileg gondolták, így a .NET összes meghatalmazottja több célmetelyt is támogat.

A meghatalmazottak használatakor leggyakrabban használt módszerek a következők:

  • Invoke(): Meghívja a delegálthoz csatolt összes metódust
  • BeginInvoke() / EndInvoke(): Aszinkron meghívási mintákhoz használatos (bár async/await most már előnyben részesítik)

Legtöbb esetben nem fogja ezeket a metódusokat közvetlenül meghívni. Ehelyett a metódushívás szintaxisát fogja használni a delegált változón, ahogy a fenti példákban is látható. Ahogy azonban a sorozat későbbi részében látni fogja, vannak olyan minták, amelyek közvetlenül ezekkel a módszerekkel működnek.

Összefoglalás

Most, hogy megismerte, hogyan képezi le a C# nyelv szintaxisa az alapul szolgáló .NET osztályokat, készen áll annak vizsgálatára, hogyan használják, hozzák létre és hívják meg az erősen típusos delegáltakat összetettebb helyzetekben.

Következő