Tuesday, December 01, 2020 21:17

Cuprins >> Delegați, Expresii Lambda, Evenimente > Delegați înlănțuiți

Delegați înlănțuiți

O proprietate utilă a obiectelor de tip delegat este aceea că mai multe obiecte pot fi atribuite unei singure instanțe delegat folosind operatorul +, proces numit înlănțuire de delegați sau delegați înlănțuiți.

Înlănțuirea delegaților nu este cu adevărat utilă până când nu vom ajunge la evenimente și abonații evenimentelor, care vor veni într-o lecție viitoare, dar este mai bine să descriu comportamentul lor acum, după ce ați văzut un pic modul de funcționare al delegaților. Așadar, să creăm un delegat, așa cum am făcut în lecțiile anterioare:

Am declarat un delegat, UnDelegat și trei metode care i se pot atribui, deoarece au aceeași semnătură: Foo(), Goo() și Sue(). Apoi, am creat o instanță a UnDelegat, numită _delegat și i-am atribuit Foo. Apoi, am invocat _delegat, i-am atribuit  Goo și l-am invocat din nou, cu acest rezultat:

Atribuire și invocare delegatNimic nou până acum, dar modul de executare, cu atribuire, invocare, apoi reatribuire și iar invocare, nu este foarte elegant. De altfel, ce se întâmplă dacă vrem să apelăm două metode în același timp, atunci când invocăm delegatul? De îndată ce îi atribuim o nouă metodă, atribuirea inițială se pierde.

Soluția acestor probleme sunt delegații înlănțuiți. Putem atribui mai multe metode aceluiași delegat, fără a suprascrie metodele atribuite anterior, creând în mod efectiv un lanț de metode atribuite acestuia, care sunt executate toate atunci când invocăm delegatul:

Este foarte ușor. Am folosit operatorul += pentru a adăuga mai multe metode la metodele deja alocate delegatului meu. Desigur, așa cum am explicat în lecția operatori, x += y este doar o formă prescurtată de a scrie x = x + y. Cu alte cuvinte, _delegat = _delegat + OMetoda. Acesta este rezultatul:

invocare de delegați înlînțuițiAșadar, lucrul important de reținut este faptul că un delegat nu face referire neapărat la o singură metodă. Putem adăuga mai multe metode, putem elimina din metodele adăugate și chiar putem adăuga o metodă de mai multe ori:

Vă puteți da seama care dintre metodele Foo vor fi eliminate atunci când instrucțiunea _delegat -= Foo va fi executată? După cum observați, Foo este atribuit delegatului de două ori, deci, care va fi eliminată, prima, ultima sau ambele?

Dacă executați codul, veți obține această imagine:

deci, ultima metodă atribuită cu acest nume este eliminată.

Să analizăm în continuare ce se întâmplă în culise atunci când compilatorul vede o operațiune de combinare a delegaților. Avem această expresie:

care de fapt înseamnă asta:

În acest caz, atunci când compilatorul întâlnește operatorul +, acesta nu efectuează de fapt adunare între delegați, ci îi combină, ceea ce înseamnă că face referire la o metodă statică numită Delegate.Combine(), care are ca argument un array de tip params Delegate, sau, în cazul nostru, preia _delegat ca prim parametru (delegatul cu care să se combine) și Goo (metoda de combinat cu) ca al doilea parametru:

În acest moment, veți primi o eroare în Visual Studio, deoarece compilatorul nu știe cu adevărat ce să facă cu Goo: tipul la timpul de compilare este Delegate (deoarece Delegate este clasa de bază a tuturor tipurilor de delegați, într-un lanț de moștenire care urmează această ierarhie: Delegate -> MulticastDelegate -> UnDelegat), iar eu îi ofer o metodă. Trebuie să-i ofer un delegat, prin urmare este convertit în acest lucru:

Cu alte cuvinte, preia un UnDelegat nou și îl combină cu _delegat, care este de asemenea de tip UnDelegat. Din nou, veți primi în continuare o eroare, iar aceasta se datorează faptului că Delegate.Combine() returnează un Delegate și încercăm să îl alocăm unei variabile de tip UnDelegat. Prin urmare, trebuie să facem un casting:

Ca o concluzie, compilatorul face destul de multe modificări asupra codului pentru ca noi să putem folosi direct operatorul +=, în loc să scriem toată instrucțiunea respectivă.

Ați putea să vă întrebați dacă e posibil să atribuiți în mod direct mai multe metode unui delegat, într-o singură atribuire, așa cum putem cu tipurile primitive. De exemplu:

În acest caz, veți primi o eroare în Visual Studio, deoarece compilatorul crede că doriți să adunați două metode împreună, ceea ce este imposibil, nu să creați o alocare a lanțului delegat. Dacă doriți să înțeleagă că dorim o înlănțuire de delegați, putem înfășura prima metodă într-un delegat:

În acest caz, compilatorul vede că declarăm un UnDelegat nou, inițializat cu Foo, iar acest delegat îl adunăm cu Goo, ceea ce ne este permis. Adăugarea ar avea de asemenea, un rezultat de tip UnDelegat, care, atunci când este adăugat la Sue, repetă procesul. În sfârșit, putem ajunge la același rezultat și efectuând castinf asupra primului operand:

În sfârșit, ce s-ar întâmpla dacă una dintre metodele înlănțate ar arunca o excepție? Cum ar proceda programul cu metodele înlănțuite rămase? Vor fi executate? Să vedem:

În acest caz, am modificat Goo pentru a arunca o excepție la executare. Să vedem rezultatul atunci când rulați programul:

Manipularea excepțiilor în delegații înlănțuiți

Deci, de fiecare dată când o metodă din lanțul de metode aruncă o excepție, toate metodele rămase nu vor fi executate. Pentru a preveni acest lucru, trebuie să modific un pic programul, ca să pot gestiona excepțiile cu un bloc Try…Catch:

Și acum totul funcționează bine și sunt de asemenea capabilă să văd mesajele de excepție, pentru că am solicitat lista de invocare a lui _delegate, iar apoi am invocat fiecare dintre aceste metode în cadrul unui bloc Try…Catch:

Delegații înlănțuiți sunt imuabili, la fel ca șirurile de caractere, ceea ce înseamnă că puteți crea delegați noi oricât doriți, dar nu îi puteți modifica pe cei existenți. Acest lucru înseamnă că atunci când creați un delegat și atribuiți o metodă, compilatorul creează un obiect în memoria Stack, care indică spre adresa acelei metode, localizată în memoria Heap. Dacă atribuiți o altă metodă delegatului, compilatorul creează de fapt un obiect nou în Stack, care indică spre metoda originală din Heap, dar indică și spre un al doilea obiect delegat, care, la rândul său, indică spre noua metoda adăugată, în timp ce obiectul delegat original din stivă este șters de colectorul de gunoi. Deci, procesul de înlănțuire al delegaților este destul de complex, în fundal.

Înlănțuirea delegaților este de obicei folosită pentru un principiu de programare numit tiparul observatorului, despre care vom învăța în viitor.

Comments

comments

Tags: , , ,

Leave a Reply



Do NOT follow this link or you will be banned from the site!