In programare, exista situatii cand trebuie sa executam o anumita actiune, dar fara sa stim in avans ce metoda sau ce obiect vom apela pentru executarea actiunii. Exemplu : la apasare, un buton va sti ca trebuie sa notifice un anumit obiect , dar nu stie exact care. Solutia simpla consta in conectarea butonului la un delegat si apoi acesta sa indice catre o anumita metoda.
Un delegat este un obiect care poate referi o metoda. Chiar daca nu este un obiect, o metoda ocupa un loc in memorie, iar aici, la aceasta adresa, va fi transmis controlul la invocarea metodei.
Un delegat reprezinta modul prin care se realizeaza comunicarea intre obiecte. Un delegat este un tip referinta si este este echivalentul unui pointer la functie din C++. Diferenta este ca delegates sunt type-safe si ca sunt orientati pe obiect.
Un delegat se declara cu ajutorul cuvantului delegate.
delegate tip-rez nume (lista-parametri);
tip-rez – tipul valorii intoarse de metodele pe care delegatul le va apela.
nume –numele delegatului.
lista-parametri – lista de parametri necesari metodelor care vor fi apelate prin intermediul delegatului.
Poate fi declarat in afara unei clase, sau in interior. In functie de cum se vrea vizibilitatea lui, ii putem aplica modificatorii de acces public, private, protected,etc.
Dupa ce a fost declarat, un delegat poate apela doar metode cu acelasi tip returnat si aceeasi lista de parametri.
Exemplu :
//declararea unui delegat delegate int StringLengthDelegate(string str); delegate string StringReverseDelegate(string str);
Intr-o clasa, construim functiile care vor fi apelate prin delegate.
class DelegateTest { //returneaza numarul de caractere al unui sir de caractere public int StringLength(string str) { Console.WriteLine("Va returna numarul de caractere"); return str.Length; } //returneaza sirul inversat public string StringReverse(string str) { string temp = ""; int i; Console.WriteLine("Inverseaza sirul."); //parcurgem sirul invers si concatenam for (i = str.Length - 1; i >= 0; i--) temp += str[i]; return temp; } }
In metoda Main :
DelegateTest test = new DelegateTest(); //construim delegat StringLengthDelegate strLength = new StringLengthDelegate(test.StringLength); //sirul care va fi transmis functiilor string str; //apelul unei metode prin intermediul delegarii convertim de la numar la string //pentru ca functia returneaza int str = strLength("Test").ToString() ; Console.WriteLine(str); //construim delegat StringReverseDelegate strReverse = new StringReverseDelegate(test.StringReverse); //apelul unei metode prin intermediul delegarii str = strReverse("Test"); Console.WriteLine(str);
Pe scurt: avem doua metode statice in clasa DelegateTest ale caror signaturi coincid cu signaturile delegatilor. In Main, construim referinte de tipul StringLengthDelegate si StringReverseDelegate, pe care le atribuim metodelor. Se mai observa ca invocarea delegatilor determina apelul metodelor.Determinarea metodei apelate se rezolva la momentul executiei, nu la compilare.
Multicasting
Multicasting-ul se defineste ca o facilitate a delegatilor si consta in capacitatea de a crea un lant de metode care vor fi automat apelate la invocarea unui delegat. Delegarile multicast trebuie sa returneze un rezultat de tip void, iar pentru crearea lor se folosesc operatorii += si –=, dupa instantierea unui delegat.
Exemplu :
//declararea unui delegat multicast delegate void MulticastDelegat(string str);
//construim delegatii MulticastDelegat multiDel; MulticastDelegate StringLength = new MulticastDelegat(test.StringLength); MulticastDelegat StringReverse = new MulticastDelegat(test.StringReverse); multiDel = StringLength; //crearea unui delegat multicast multiDel += StringReverse;
Datorita faptului ca la compilare, nu se cunosc metodele care urmeaza a fi executate, lucrul cu delegate este intalnit in arhitecturile care permit adaugarea componentelor pe parcurs.
In articolul urmator, se va observa una din utilitatile delegatilor, anume implementarea evenimentelor.