You are on page 1of 54

Delegates

Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 1/54


Asiminoaei Ioan
Delegates

Cuprins

Partea a - I - a

Ce este un delegate?
Definirea unui delegate.
Clasele de baza System.MulticastDelegate si System.Delegate.
Invocarea metodelor callback.
Covarianta (relaxed delegates) delegates.
Liste de delegates.
Delegates si Reflection.
Exemple

Partea a II - a

Apel sincron. Apel asincron.
Delegates anonimi.
Expresii lambda.
Evenimente
Pattern-ul Publish / Subscribe
Exemple.

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 2/54
Asiminoaei Ioan
Ce este un delegate ?

In esenta, un delegate este un obiect type-safe ce puncteaza la o metoda (sau posibil o
lista de metode) din cadrul aplicatiei, metoda ce poate fi invocata mai tarziu. Un obiect
delegate mentine urmatoarele informatii :
Adresa metodei pe care se face apel;
Argumentele (daca exista) acestei metode;
Valoarea de retur (daca exista) a acestei metode.

Dupa ce am creat delegate si am furnizat informatiile necesare, acesta poate invoca in
mod dinamic metoda (metodele) la care puncteaza.

Metodele pot fi apelate sincron sau asincron.

Definirea unui delagate in C#, exemplu:

public delegate void Feedback( Object value,
Int32 item,
Int32 numItems);

Prototipul metodei ce poate fi apelat cu acest delegate este:

public void Nume_Metoda(object value, int item, int numItems) ;

Cand compilatorul proceseaza acest cod, va genera o clasa sealed derivata din
System.MulticastDelegate.

Clasa poate avea urmatorul prototip (documentatie Microsoft):

class sealed Feedback : System.MulticastDelegate
{
// Constructor
public Feedback(Object target, Int32 methodPtr);

// Metoda cu acelasi prototip ca cel specificat
// de codul sursa

public void Invoke( Object value,
Int32 item,
Int32 numItems);

// Metode ce permit apelul asincron
public virtual IAsyncResult BeginInvoke(
Object value,
Int32 item,
Int32 numItems,
AsyncCallback callback,
Object object);

public virtual void EndInvoke(IAsyncResult result);
}
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 3/54
Asiminoaei Ioan

Observatie
Ce e cu rosu reprezinta parametrii metodei callback. Metodele Invoke si EndInvoke
returneaza void in acest caz.

Metoda Invoke nu o vom apela explicit din cod, desi se poate face si acest lucru (se
apeleaza pe un obiect de tip delegate).

Delegates pot puncta la metode ce contin parametri prefixati cu ref si out sau un
array de parametri marcati cu cuvantul cheie params.

Exemplu

public delegate string AltDelegate(out bool a,
ref bool b,
int c);

clasa generata arata cam asa:

sealed class AltDelegate : System.MulticastDelegate
{
public AltDelegate (object target, uint functionAddress);
public string Invoke(out bool a, ref bool b, int c);
public IAsyncResult BeginInvoke(out bool a, ref bool b, int c,
AsyncCallback cb, object state);
public string EndInvoke(out bool a, ref bool b,
IAsyncResult result);
}

Observatie
Metodele Invoke si EndInvoke returneaza string in acest caz. Observati modul de
creare al clasei AltDelegate.

Pattern-ul de baza folosit de C# la intalnirea unui delegate poate fi urmatorul :

// pseudo-code
public sealed class DelegateName : System.MulticastDelegate
{
// constructor
public DelegateName (object target, uint functionAddress);

// metoda Invoke pentru apel sincron
public delegateReturnValue Invoke(
allDelegateInputRefAndOutParams );

// metoda pentru apel asincron
public IAsyncResult BeginInvoke(
allDelegateInputRefAndOutParams,
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 4/54
Asiminoaei Ioan
AsyncCallback cb, object state);

// metoda pentru terminarea apelului asincron
public delegateReturnValue EndInvoke(
allDelegateRefAndOutParams,
IAsyncResult result);
}
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 5/54
Asiminoaei Ioan

Clasele de baza System.MulticastDelegate si System.Delegate

O descriere completa o gasiti in MSDN.

public abstract class MulticastDelegate : Delegate
{
// Returns the list of methods "pointed to."
public sealed override Delegate[] GetInvocationList();

// Overloaded operators.
public static bool operator ==(MulticastDelegate d1,
MulticastDelegate d2);
public static bool operator !=(MulticastDelegate d1,
MulticastDelegate d2);

// Used internally to manage the list of methods
// maintained by the delegate.
private IntPtr _invocationCount;
private object _invocationList;
}

public abstract class Delegate : ICloneable, ISerializable
{
// Methods to interact with the list of functions.
public static Delegate Combine(
params Delegate[] delegates);
public static Delegate Combine(Delegate a, Delegate b);

public static Delegate Remove(Delegate source,
Delegate value);
public static Delegate RemoveAll(Delegate source,
Delegate value);

// Overloaded operators.
public static bool operator ==(Delegate d1, Delegate d2);
public static bool operator !=( Delegate d1, Delegate d2);

// Properties that expose the delegate target.
public MethodInfo Method { get; }
public object Target { get; }
}

Campuri principale din System.MulticastDelegate
Camp Tip Descriere
_target System.Object Se refera la obiectul pe care se va lucra cand
metoda callback va fi apelata. Acest camp
este folosit pentru metode callback ale
instantei.
_methodPtr System.Int32 Un intreg folosit intern de CLR pentru a
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 6/54
Asiminoaei Ioan
identifica metoda callback apelata.
_prev System.MulticastDelegate Se refera la un alt obiect delegate. In mod
normal are valoarea null. Este folosit pentru
a crea o lista inlantuita de obiecte
MulticastDelegate.

Constructorii pentru delegate au doi parametri: o referinta la un object si un intreg ce
identifica metoda callback.

Pentru metodele instantei, _target retine referinta la obiect, iar pentru metodele
callback statice, _target = null.

MulticastDelegate defineste doua proprietati: Target si Method.

Proprietatea Target returneaza o referinta la obiectul pe care se va lucra daca metoda
callback apelata este o metoda a instantei. Daca metoda este statica Target returneaza
null.

Proprietatea Method returneaza un obiect System.Reflection.MethodInfo, obiect
ce identifica metoda callback.

Invocarea metodelor callback

Exemplu. Metoda ProcessItems are ca parametru un delegate de tip FeedBack definit la
inceputul cursului. Metoda are ca parametru o referinta la o metoda (in C asta inseamna
pointer la o functie).

public void ProcessItems(Feedback feedback)
{
for (Int32 item = 0; item < items.Length; item++)
{
if (feedback != null)
{
//
// Daca s-a specificat o functie callback,
// atunci o apelam.
//

feedback(items[item], item, items.Length);
}
}
}

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 7/54
Asiminoaei Ioan

Un alt exemplu simplu de delegate (Andrew Troelsen)

namespace SimpleDelegate
{
// Acest delegate poate puncta la orice metoda ce are ca
// parametri doi intregi si returneaza un intreg.

public delegate int BinaryOp(int x, int y);

public class SimpleMath
{
public static int Add(int x, int y)
{ return x + y; }
public int Subtract(int x, int y)
{ return x - y; }
}

class Test
{
static void Main(string[] args)
{
Console.WriteLine(" - Exemplu Delegate -\n");

// Cream un obiect BinaryOp ce puncteaza la
// SimpleMath.Add() Metoda statica

BinaryOp b = new BinaryOp(SimpleMath.Add);

// Invocam metoda Add (indirect) folosind obiectul
// delegate.

Console.WriteLine("10 + 10 = {0}", b(10, 10));

// Metoda a instantei

SimpleMath s = new SimpleMath();
BinaryOp dif = new BinaryOp(s.Subtract);
Console.WriteLine(" 20 10 = {0}", dif(20,10));
Console.ReadLine();
}
}
}

Observatie

In loc de apelul

int result = b(10,10) ;

putem scrie si

int result = b.Invoke(10, 10);

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 8/54
Asiminoaei Ioan

Vezi exemplele cu delegates (proiect ...)

class A
{
public delegate void D1(string msg);
public delegate void D2(string msg);

private D1 d1_delegat;
private D2 d2_delegat;

public void OnD1(D1 metodaClient)
{
d1_delegat = metodaClient;
}

public void OnD2(D2 metodaClient)
{
d2_delegat = metodaClient;
}

public void ApelDelegates()
{
...
if (d1_delegat != null)
d1_delegat("D1!");
...
if (d2_delegat != null)
d2_delegat("D2");
}
}

Codul din metoda Main poate fi:

A a = new A();
a.OnD1(new A.D1(MainD1)); // d1_delegat = MainD1
a.OnD2(new A.D2(MainD2));
a.ApelDelegates();

si metodele MainD1, MainD2 in aceeasi clasa cu metoda Main

public static void MainD1(string msg)
{
Console.WriteLine(msg);
}

public static void MainD2(string msg)
{
Console.WriteLine(msg);
}


Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 9/54
Asiminoaei Ioan
Covarianta si contravarianta in .NET

Exemplele pentru varianta se bazeaza in marea majoritate a cazurilor pe MSDN.

Interfete generice variant .

Putem declara parametri de tip generic in interfete ca fiind covariant sau contravariant.

Covarianta permite metodelor din interfata sa poata returna tipuri ce sunt mai jos in
ierarhia de derivare decat cele definite de parametrii de tip generic.

Contravarianta permite metodelor din interfata sa aiba tipuri de argumente ce sunt mai
sus in ierarhie decat cel specificat prin parametrul generic.

Observatie
Clasa de baza se considera mai jos in ierarhia de derivare.

O interfata generica ce are parametri de tip generic covarianti sau contravarianti se
numeste variant .

O interfata care nu e nici covarianta nici contravarianta se numeste invarianta.

Interfetele generice variant se declara folosind cuvintele cheie in si out pentru
parametrii generici.
out : parametru de tip generic covariant;
in : parametru de tip generic contravariant;
Regula pentru out:
Tipul este folosit numai ca tip returnat de metodele din interfata si nu este folosit ca tip
al argumentelor metodelor.

Exemplu:
interface ICovariant<out R>
{
// Corect
R GetSomething();

// Urmatoarea instructiune genereaza o eroare la compilare.

// void SetSometing(R sampleArg);
}
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 10/54
Asiminoaei Ioan

Observatie :
Daca avem un delegate generic contravariant ca parametru al metodei, se poate folosi
tipul ca parametru generic pentru delegate.

Exemplu

interface ICovariant<out R>
{
void DoSomething(Action<R> callback);
}

Regula pentru out:
Tipul nu este folosit drept constrangere generica pentru metodele din interfata:

interface ICovariant<out R>
{
// Urmatoarea instructiune genereaza o eroare la compilare
// deoarece putem folosi in constrangeri generice numai
// tipuri contravariante sau invariante.

// void DoSomething<T>() where T : R;
}

in: declarare parametru de tip generic ca fiind contravariant. Poate fi folosit pentru
constrangeri generice.

Tipul contravariant poate fi folosit numai ca un tip al argumetelor metodelor si nu ca
valoare de retur.

interface IContravariant<in A>
{
void SetSomething(A sampleArg);
void DoSomething<T>() where T : A;

// Urmatoarea instructiune genereaza o eroare la compilare

// A GetSomething();
}

E posibil sa folosim covarianta si contravarianta in aceeasi interfata, dar pentru
parametri diferiti (nu e posibil sa avem in si out pentru acelasi parametru).

interface IVariant<out R, in A>
{
R GetSomething();
void SetSomething(A sampleArg);
R GetSetSometings(A sampleArg);
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 11/54
Asiminoaei Ioan
}

Exemple:

interface ICovariant<out R>
{
R GetSomething();
}
class SampleImplementation<R> : ICovariant<R>
{
public R GetSomething()
{
// Cod ...
return default(R);
}
}

Clasele ce implementeaza interfete variante sunt invariante.

// Interfata este covarianta.
ICovariant<Button> ibutton = new SampleImplementation<Button>();
// Object este mai jos in ierarhia de derivare;
// este clasa de baza (direct sau indirect) pentru Button.
ICovariant<Object> iobj = ibutton;

// Clasa este invarianta (vedeti observatia de mai sus).
SampleImplementation<Button> button = new
SampleImplementation<Button>();

// Urmatoarea instructiune genereaza o eroare la compilare
// deoarece clasele sunt invariante.

// SampleImplementation<Object> obj = button;

Varianta in interfete generice (C#)
Suportul pentru varianta permite conversia implicita a claselor ce implementeaza aceste
interfete.
.NET Framework 4 introduce suport pentru varianta pe interfetele urmatoare.
IEnumerable <T> (T is covariant)
IEnumerator <T> (T is covariant)
IQueryable <T> (T is covariant)
IGrouping <TKey, TElement> ( TKey and TElement are
covariant)
IComparer <T> (T is contravariant)
IEqualityComparer <T> (T is contravariant)
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 12/54
Asiminoaei Ioan
IComparable <T> (T is contravariant)

Exemplu (MSDN) :

// Ierarhie simpla de clase
class BaseClass { }
class DerivedClass : BaseClass { }

// Clasa de comparare.
// Interfata IEqualityComparer este definita in
// System.Collection.Generics astfel
// public interface IEqualityComparer<in T>
// adica tipul parametrului este contravariant
class BaseComparer : IEqualityComparer<BaseClass>
{
// metode din interfata IEqualityComparer
public int GetHashCode(BaseClass baseInstance)
{
return baseInstance.GetHashCode();
}
public bool Equals(BaseClass x, BaseClass y)
{
return x == y;
}
}
class Program
{
static void Test()
{
IEqualityComparer<BaseClass> baseComparer =
new BaseComparer();

// Conversie implicita de la IEqualityComparer<BaseClass>
// la IEqualityComparer<DerivedClass>.

IEqualityComparer<DerivedClass> childComparer =
baseComparer;
}
}

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 13/54
Asiminoaei Ioan
Covarianta si contravarianta in delegates

.NET Framework 3.5 , 4.0 si Visual Studio 2008 au introdus suport pentru
varianta pentru determinarea semnaturii metodelor cu tipurile delegate, adica putem
atribui la delegates nu numai metode ce se potrivesc cu semnatura delegate-ului, dar si
metode ce returneaza tipuri situate mai jos in ierarhia de derivare (covarianta) sau
accepta parametri ce sunt de un tip situat mai sus in ierarhia de derivare (contravarianta)
fata de tipul specificat de delagate.
Aceasta include atat delegates generici cat si delegates non-generici.

Sa urmarim aceste lucruri in exemplele de mai jos.

public class First { }
// Clasa Second este situata mai sus in ierarhia de derivare
public class Second : First { }

// Definim doi delegates: unul non-generic si unul generic
public delegate First SampleDelegate(Second a);
public delegate R SampleGenericDelegate<A, R>(A a);

iar metodele atribuite pot fi:

// Se potriveste cu semnataura
public static First ASecondRFirst(Second first)
{ return new First(); }

// Tipul returnat este mai derivat.
public static Second ASecondRSecond(Second second)
{ return new Second(); }

// Tipul argumentului este mai putin derivat.
public static First AFirstRFirst(First first)
{ return new First(); }

// Tipul returnat este mai derivat si
// tipul argumentului este mai putin derivat
public static Second AFirstRSecond(First first)
{ return new Second(); }

Urmatorul exemplu ilustreaza conversia implicita intre semnataura metodei si tipul
delegate.

// Atribuim o metoda ce se potriveste ca semnatura la
// un delegate non-generic.
// Nu e necesara conversia.
SampleDelegate dNonGeneric = ASecondRFirst;

// Atribuim o metoda cu un tip returnat mai derivat
// si tipul argumentului mai putin derivat la
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 14/54
Asiminoaei Ioan
// un delegate non-generic.
// Se foloseste conversia implicita.
SampleDelegate dNonGenericConversion = AFirstRSecond;

// Atribuim o metoda ce se potriveste ca semnataura
// la un delegate generic.
// Nu e necesara conversia.
SampleGenericDelegate<Second, First> dGeneric = ASecondRFirst;

// Atribuim o metoda cu un tip returnat mai putin derivat
// si tipul argumentului mai derivat la
// un delegate generic.
// Se foloseste conversia implicita.
SampleGenericDelegate<Second, First> dGenericConversion =
AFirstRSecond;


Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 15/54
Asiminoaei Ioan
Delegate - Varianta in parametri de tip generic

In .NET Framework 4, putem realiza conversia implicita intre delegates, astfel
incat delegates generici ce au specificat tipuri diferite ca parametri generici pot fi atribuiti
unul altuia cu conditia ca tipurile sa fie mostenite unul din altul asa cum e cerut de
varianta.
Cuvintele cheie sunt:
in pentru delegate contravariant;
out pentru delegate covariant.

// Tipul T este declarat covariant folosind cuvantul cheie out.
public delegate T SampleGenericDelegate <out T>();

public static void Test()
{
SampleGenericDelegate <String> dString = () => " ";

// Putem face urmatoarea atribuire deoarece T este marcat
// cu out (covariant) si Object este mai jos in ierarhie
// decat String
SampleGenericDelegate <Object> dObject = dString;
}
In exemplul urmator, SampleGenericDelegate<String> nu poate fi convertit in mod
explicit la SampleGenericDelegate<Object>, desi String este derivata din Object.
Problema se rezolva daca marcam parametrul generic T cu out.
public delegate T SampleGenericDelegate<T>();

public static void Test()
{
SampleGenericDelegate<String> dString = () => " ";

// You can assign the dObject delegate
// to the same lambda expression as dString delegate
// because of the variance support for
// matching method signatures with delegate types.
SampleGenericDelegate<Object> dObject = () => " ";

// The following statement generates a compiler error
// because the generic type T is not marked as covariant.

// SampleGenericDelegate <Object> dObject = dString;
}
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 16/54
Asiminoaei Ioan
Delegates predefiniti in .NET
.NET Framework 4 introduce suport pentru varianta pentru parametri de tip generic in
urmatorii delegates:
Delegate Action din namespace System, de exemplu, Action <T > si
Action <T1, T2 >
Delegates Func din namespace System, de exemplu, Func <TResult > si
Func <T, TResult >
Delegate Predicate <T >
Delegate Comparison <T >
Delegate Converter <TInput, TOutput >
Daca un delegate generic are parametri generici covarianti sau contravarianti, poate fi
referit ca un delegate generic variant.
Exemplu delegate covariant:
public delegate R DCovariant<out R>();
Exemplu delegate contravariant:
public delegate void DContravariant<in A>(A a);
Exemplu delegate covariant si contravariant
public delegate R DVariant<in A, out R>(A a);

Exemplu : cast de la clasa de baza la clasa derivata

In cadrul unei ierarhii de clase construim un singur delegate ce va puncta la metode ce
returneaza tipuri de clase conform regulilor de mostenire a claselor.

Consideram clasa B derivata din A.

class A {}

class B : A
{}

iar in clasa de test putem scrie :

// Definim un delegate ce va puncta la o metoda fara parametri
// si care returneaza o instanta a tipului A.

public delegate A ObtinA();
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 17/54
Asiminoaei Ioan

public static A GetA()
{
return new A();
}
public static B GetB()
{
return new B();
}

static void Main(string[] args)
{
Console.WriteLine("***** Covarianta Delegate *****\n");
ObtinA targetA = new ObtinA(GetA);
A a = targetA();
Console.WriteLine("Am obtinut {0}", a);

// Covariance permite aceasta atribuire.
// Atentie! ObtinA returneaza un obiect de tip A
// Il folosim aici pt a obtine o instanta a clasei B
ObtinA targetB = new ObtinA(GetB);
B b = (B)targetB();
Console.WriteLine("Am obtinut {0}", b);
}


Un exemplu complet cu varianta delegates bazat pe o ierarhie de clase

Consideram urmatoarea ierarhie de clase.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Delegate
{
class Persoana { }

class Client : Persoana { }

class Salariat : Persoana { }

class Manager : Salariat{ }

}

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 18/54
Asiminoaei Ioan
Persoana
Class
Client
Persoana
Class
Salariat
Persoana
Class
Manager
Salariat
Class


In clasa de test avem urmatorul cod :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Delegate
{
class Program
{
delegate T Actiune<T>();

static void Main(string[] args)
{
// incercare covarianta
Actiune<Client> getClient = () => new Client();

// Linia urmatoare nu se compileaza
Actiune<Persoana> getPersoana = getClient;
}
}
}

Rezolvare: se declara delegate astfe (covariant)l:

delegate T Actiune<out T>();

Acum getPersoana va returna un Client care este derivat din Persoana.
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 19/54
Asiminoaei Ioan

Covarianta (out) si contravarianta (in)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Delegate
{
class Program
{
delegate T Actiune<out T>();

// Delegate pentru demo contravarianta
// Tipul T nu e declarat cu in, deci contravarianta
// nu va functiona
delegate void ActiuneContravarianta<T>(T t);

static void Main(string[] args)
{
// incercare covarianta
Actiune<Client> getClient = () => new Client();
Actiune<Persoana> getPersoana = getClient;

// incercare contravarianta

ActiuneContravarianta<Persoana> printPersoana =
(persoana) => Console.WriteLine(persoana);

// Nu se compileaza
ActiuneContravarianta<Client> printClient =
printPersoana;

Console.ReadLine();
}
}
}

Varianta corecta: delegate void ActiuneContravarianta<in T>(T t);

Codul complet si corect este dat in continuare.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Delegate
{
class Program
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 20/54
Asiminoaei Ioan
{
delegate T Actiune<out T>();

delegate void ActiuneContravarianta<in T>(T t);

static void Main(string[] args)
{
// incercare covarianta
Actiune<Client> getClient = () => new Client();
Actiune<Persoana> getPersoana = getClient;

// incercare contravarianta

ActiuneContravarianta<Persoana> printPersoana =
(persoana) => Console.WriteLine(persoana);

// Se compileaza acum
ActiuneContravarianta<Client> printClient =
printPersoana;

Console.ReadLine();
}
}
}


Daca vom incerca in Main ceva de genul:

ActiuneContravarianta<Salariat> printSalariat =
(salariat) => Console.WriteLine(salariat);
ActiuneContravarianta<Client> printClient2 = printSalariat;

nu va functiona deoarece Client nu este derivat din Salariat.
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 21/54
Asiminoaei Ioan
Curs 6

Liste de delegates (Delegates Chains)

Pe acelasi obiect apelam mai multe metode callback. Campul _prev din
MulticastDelegate este folosit pentru a mentine lista inlantuita de delegate.

Clasa Delegate defineste urmatoarele metode pe care le putem folosi in gestionarea
listei inlantuite de obiecte delegate.

class System.Delegate
{
// Combines the chains represented by head and tail;
// head is returned.
// NOTE: head will be the last delegate called.

public static Delegate Combine(Delegate tail,
Delegate head);

// Creates a chain represented by the array of delegates.
// NOTE: entry 0 is the head
// and will be the last delegate called.

public static Delegate Combine(Delegate[] delegateArray);

// Removes a delegate matching values
// Target/Method from the chain.
// The new head is returned and will be the
// last delegate called.

public static Delegate Remove(Delegate source,
Delegate value);
}

Cand se construieste un obiect delegate, campul _prev = null. Pentru a construi lista
inlantuita de delegate folosim metodele Combine, ca mai jos :

class A
{
public delegate void D1(string msg);
public delegate void D2(string msg);

private D1 d1_delegat;
private D2 d2_delegat;

public void OnD1(D1 metodaClient)
{
d1_delegat += metodaClient; // Combine
}
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 22/54
Asiminoaei Ioan

public void OnD2(D2 metodaClient)
{
d2_delegat += metodaClient; // Combine
}

public void ApelDelegates()
{
...
if (d1_delegat != null)
d1_delegat("D1!");
...
if (d2_delegat != null)
d2_delegat("D2");
}
}

in Main

A a = new A();
a.OnD1(new A.D1(MainD1));
a.OnD2(new A.D2(MainD2));
a.OnD1(new A.D1(MainD1_1));
a.ApelDelegates();

si metodele MainD1, MainD2 in aceeasi clasa cu metoda Main

public static void MainD1(string msg)
{
Console.WriteLine(msg);
}

public static void MainD1_1(string msg)
{
Console.WriteLine(msg);
}

public static void MainD2(string msg)
{
Console.WriteLine(msg);
}


Operatorul += este sinonim cu Combine, iar -= este sinonim cu Remove.


Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 23/54
Asiminoaei Ioan
Mai mult control asupra metodei Invoke

Algoritmul de executie al metodleor callback este unul serial.

In momentul cand se folosesc lanturi de delegates, valoarea returnata este data de
ultimul delegate ce se executa. Acest lucru poate constitui in unele cazuri o problema.

O alta situatie, in cazul lanturilor de delegates, este cea data de generarea unei
exceptii sau executia unei metode lente in cazul unei metode callback.

Pentru situatiile cand algoritmul de executie al metodelor callback (algoritm serial) nu
este suficient de bun, putem folosi metoda de instanta GetInvocationList din
MulticastDelegate. Aceasta metoda o putem folosi pentru a apela fiecare delegate in
mod explicit dintr-o lista de delegates. In fapt aceasta metoda returneaza o colectie de
delegates, colectie pe care o iteram si executam fiecare delegate din colectie.

public class MulticastDelegate
{
// Creates a delegate array; each item is a clone from the chain.
// NOTE: entry 0 is the tail, which would normally
// be called first.

public virtual Delegate[] GetInvocationList();
}

Metoda GetInvocationList opereaza cu referinte la delegate si returneaza un tablou
de referinte la delegates. Se creaza o clona pentru fiecare obiect delegate din lista.
Fiecare clona are campul _prev = null.

Exemplu de folosire

delegate String GetStatus();

//
// Delegate[] arrayOfDelegates =
// status.GetInvocationList();

// Declar o lista vida de delegate.
GetStatus getStatus = null;

//Construiesc lista de delegates

getStatus += new GetStatus(new A().MetodaA);
getStatus += new GetStatus(new B().MetodaB);
getStatus += new GetStatus(new C().MetodaC);

string results = GetResults(getStatus);

Construiesc functia ce apeleaza aceasta lista de delegates si returneaza rezultatele.
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 24/54
Asiminoaei Ioan

static String GetResults(GetStatus status)
{
if (status == null) return null;

StringBuilder res = new StringBuilder();

// Obtinem lista de delegates

Delegate[] ad = status.GetInvocationList();

// Iteram pe fiecare delegate din lista
foreach (GetStatus getStatus in ad)
{
try
{
//Construim rezultatul

res.AppendFormat("{0}{1}{1}",
getStatus(), Environment.NewLine);
}
catch (Exception e)
{
// exceptie
res.AppendFormat(Exceptie... +
e.Message);
}
}
// Returnam rezultatul

return res.ToString();
}

Observatie
Folosind aceasta tehnica putem controla fiecare delegate si putem reactiona (scriind
cod) pentru a trata exceptiile.
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 25/54
Asiminoaei Ioan
Delegate si Reflection

In metodele callback discutate pana acum, informatia necesara (parametrii) erau stiuti in
momentul compilarii.

Tratam problema completarii acestor parametri in momentul executiei plus problema
apelarii unei anumite metode callback. Este exact ceea ce intimpla in programarea
Windows. Un eveniment genereaza un mesaj, daca exista un handler pentru mesaj acesta
va fi tratat (va fi apelata metoda asociata acestui eveniment), altfel nu.

Metodele folosite sunt CreateDelegate si DynamicInvoke.

Metodele din clasa delegate ce realizeaza acest lucru sunt (consultati MSDN):

Name
Description
CreateDelegate(Type,
MethodInfo)
Creates a delegate of the specified type to represent the
specified static method.
CreateDelegate(Type,
Object, MethodInfo)
Creates a delegate of the specified type that represents
the specified static or instance method, with the
specified first argument.
Starting with the .NET Framework version 2.0 Service
Pack 1, this method can be used to access non-public
methods if the caller has been granted
ReflectionPermission with the ReflectionPermissionFlag
.RestrictedMemberAccess flag and if the grant set of the
non-public methods is restricted to the callers grant set,
or a subset thereof. (See Security Considerations for
Reflection.)
To use this functionality, your application should target
the .NET Framework version 3.5 or later.

CreateDelegate(Type,
Object, String)
Creates a delegate of the specified type that represents
the specified instance method to invoke on the specified
class instance.
CreateDelegate(Type,
MethodInfo, Boolean)
Creates a delegate of the specified type to represent the
specified static method, with the specified behavior on
failure to bind.
CreateDelegate(Type,
Type, String)
Creates a delegate of the specified type that represents
the specified static method of the specified class.
CreateDelegate(Type,
Object, MethodInfo,
Boolean)
Creates a delegate of the specified type that represents
the specified static or instance method, with the
specified first argument and the specified behavior on
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 26/54
Asiminoaei Ioan
failure to bind.
CreateDelegate(Type,
Object, String,
Boolean)
Creates a delegate of the specified type that represents
the specified instance method to invoke on the specified
class instance with the specified case-sensitivity.
CreateDelegate(Type,
Type, String, Boolean)
Creates a delegate of the specified type that represents
the specified static method of the specified class, with
the specified case-sensitivity.
CreateDelegate(Type,
Object, String,
Boolean, Boolean)
Creates a delegate of the specified type that represents
the specified instance method to invoke on the specified
class instance, with the specified case-sensitivity and the
specified behavior on failure to bind.
CreateDelegate(Type,
Type, String, Boolean,
Boolean)
Creates a delegate of the specified type that represents
the specified static method of the specified class, with
the specified case-sensitivity and the specified behavior
on failure to bind.

si in final

public class Delegate
{
// Invocare metoda callback a obiectului delegate,
// pasindu-i parametrii la runtime
public Object DynamicInvoke(Object[] args);
}

Toate metodele CreateDelegate construiesc un obiect nou de tip Delegate,
identificat de primul parametru de tip Type.
Ceilalti parametri determina metoda callback pe care obiectul derivat din Delegate o va
implementa precum si informatii auxiliare.

Metoda DynamicInvoke ne permite sa invocam o metoda callback a obiectului
delegate, pasindu-i parametrii la runtime.

DynamicInvoke face un control al parametrilor (acelasi numar, acelasi tip, aceeasi
pozitie) pentru metoda callback invocata. In caz de esec se genereaza o exceptie.

DynamicInvoke returneaza obiectul returnat de metoda callback.

Exemplu (MSDN)

using System;
using System.Reflection;
using System.Security.Permissions;

// Declare three delegate types for demonstrating
// the combinations of static versus
// instance methods and open versus closed delegates.
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 27/54
Asiminoaei Ioan
//

public delegate void D1(C c, string s);
public delegate void D2(string s);
public delegate void D3();

//
// A sample class with an instance method and a static method.
//
public class C
{
private int id;
public C(int id) { this.id = id; }

public void M1(string s)
{
Console.WriteLine("Instance method M1 on C: id = {0},
s = {1}",this.id, s);
}

public static void M2(string s)
{
Console.WriteLine("Static method M2 on C: s = {0}", s);
}
}
// End of class C

// Clasa de test
public class Example
{
public static void Main()
{
C c1 = new C(42);

// Get a MethodInfo for each method.

MethodInfo mi1 = typeof(C).GetMethod("M1",
BindingFlags.Public | BindingFlags.Instance);

// metoda statica
MethodInfo mi2 = typeof(C).GetMethod("M2",
BindingFlags.Public | BindingFlags.Static);

D1 d1; // D1(C c, string s)
D2 d2; // D2(string s)
D3 d3; // D3()


Console.WriteLine("\nAn instance method closed over C.");

// Trebuie sa furnizam instanta clasei ce contine
// metoda callback
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 28/54
Asiminoaei Ioan
// In this case, the delegate and the
// method must have the same list of argument types; use
// delegate type D2 with instance method M1.
//
Delegate test =
Delegate.CreateDelegate(typeof(D2), c1, mi1, false);

// Because false was specified for throwOnBindFailure
// in the call to CreateDelegate, the variable 'test'
// contains null if the method fails to bind (for
// example, if mi1 happened to represent a method of
// some class other than C).
//
if (test != null)
{
d2 = (D2)test; // atribuire metoda callback

// The same instance of C is used every time the
// delegate is invoked.
d2("Hello, World!");
d2("Hi, Mom!");
}


Console.WriteLine("\nAn open instance method.");

// Cream delegate fara a furniza instanta clasei
// ce contine metoda callback. Instanta clasei o
// vom furniza in momentul apelului metodei callback.

// In this case, the delegate has one more
// argument than the instance method; this argument comes
// at the beginning, and represents the hidden instance
// argument of the instance method. Use delegate type D1
// with instance method M1.
//
d1 = (D1) Delegate.CreateDelegate(typeof(D1), null, mi1);

// An instance of C must be passed in each time the
// delegate is invoked.
//
d1(c1, "Hello, World!");
d1(new C(5280), "Hi, Mom!");


Console.WriteLine("\nAn open static method.");

// In this case, the delegate and the method must
// have the same list of argument types;
// use delegate type
// D2 with static method M2.
//
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 29/54
Asiminoaei Ioan

d2 = (D2) Delegate.CreateDelegate(typeof(D2), null, mi2);

// No instances of C are involved,
// because this is a static method.
//

d2("Hello, World!");
d2("Hi, Mom!");


Console.WriteLine("\nA static method closed +
over the first argument (String).");

// The delegate must omit the first argument
// of the method.
// A string is passed as the firstArgument parameter, and
// the delegate is bound to this string.
// Use delegate type D3 with static method M2.
//

d3 = (D3) Delegate.CreateDelegate(typeof(D3),
"Hello, World!", mi2);

// Each time the delegate is invoked, the same string is
// used.
d3();
}
}

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 30/54
Asiminoaei Ioan
Creare delegates generici

Studiu de caz

Un delegate ce mapeaza o functie ce are un singur parametru si returneaza void.

Observatie
Vedeti si alte situatii date mai jos ca exemplu.

Daca parametrul este diferit de la metoda la metoda putem construi urmatorul delegate
generic :

namespace GenericDelegate
{

public delegate void GenericDelegate<T>(T arg);

class Program
{
static void Main(string[] args)
{
Console.WriteLine("***** Generic Delegates *****\n");

// Specificam o metoda ce are ca parametru un string
GenericDelegate<string> strTarget =
new GenericDelegate<string>(StringTarget);
strTarget("Some string data");

// Specificam o metoda ce are ca parametru un int
GenericDelegate<int> intTarget =
new MyGenericDelegate<int>(IntTarget);
intTarget(9);
Console.ReadLine();
}
static void StringTarget(string arg)
{
Console.WriteLine("arg in uppercase is: {0}",
arg.ToUpper());
}
static void IntTarget(int arg)
{
Console.WriteLine("++arg is: {0}", ++arg);
}
}
}

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 31/54
Asiminoaei Ioan
Mai multe exemple:

namespace DelegateAsincronAndGeneric
{
public delegate void ActiuneDelegate<T>(T value);

public delegate T DelegateT<T>(T t);

public delegate U DelegateTU<T, U>(T t);

class CreateDelegate
{
public ActiuneDelegate<string> md;

public DelegateT<string> dt;

public DelegateTU<string, int> udt;

public int MIntStr(string str)
{
return str.Length;
}

public string MetGenDel(string str)
{
return "Modificat " + str;
}

public void Metoda(string str)
{
Console.WriteLine("CreateDelegate.Metoda {0}", str);
}

// nu e necesar sa punem in ctor
// E numai in scop demonstrativ
//
public CreateDelegate(string str)
{
md = this.Metoda;
dt = this.MetGenDel;
udt = this.MIntStr;
}
}
}

In clasa de test putem scrie:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 32/54
Asiminoaei Ioan

namespace DelegateAsincronAndGeneric
{
class Program
{
static void Main(string[] args)
{
CreateDelegate cd = new CreateDelegate("Info");
//
// Schimbam metoda callback pt ActiuneDelegate
//

cd.md = (str) =>
{
Console.WriteLine("Delegate Lambda: " +str);
};
cd.md("Iasi");

//
// Pentru DelegateTU atasam metoda MIntStr
// Este metoda a instantei deci avem nevoie de
// instanta tipului unde este definita metoda

Program p = new Program();
cd.udt = p.MIntStr;

//
// Pentru DelegateT metoda este cea furnizata in clasa
// CreateDelegate si atasata in ctor
//
Console.WriteLine(
cd.dt("Vaslui") +
" Length = " + cd.udt("Vaslui"));
}

public int MIntStr(string str)
{
return str.Length + 20;
}
}
}

si un delegate cu doi parametri generici:

public delegate DelegateEventHandler<T,U>(T sender,
U eventArgs) ;


Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 33/54
Asiminoaei Ioan
Simulare delegates generici fara a folosi generic

Putem face acest lucru prin a crea un delegate ce mapeaza o functie ce are un parametru
de tip Object. In acest caz exista boxing si unboxing.

public delegate void NonGenericDelegate(object arg);

class Program
{
static void Main(string[] args)
{
...
MyDelegate d = new NonGenericDelegate(MyTarget);
d("More string data");

MyDelegate d2 = MyTarget;
d2(9); // Penalizare boxing.
Console.ReadLine();
}

// Trebuie sa facem conversie la tipul pe care il asteptam
static void MyTarget(object arg)
{
if(arg is int)
{
int i = (int)arg; // Penalizare unboxing.
Console.WriteLine("++arg is: {0}", ++i);
}
if(arg is string)
{
string s = (string)arg; // Nu avem unboxing
Console.WriteLine("arg in uppercase is: {0}",
s.ToUpper());
}
}
}

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 34/54
Asiminoaei Ioan

Evenimente

Membrul event al unei clase permite acesteia de a notifica alte obiecte ca ceva s-a
intamplat.

Un tip ce defineste un membru event are urmatoarele capabilitati:

capabilitatea obiectelor de a-si inregistra interesul lor pentru un evenimet;
capabilitatea de a renunta la un eveniment (stergerea inregistrarii);
capabilitatea obiectului ce defineste evenimentul de a mentine o lista a obiectelor
inregistrate pentru eveniment si de a le notifica acestora producerea
evenimentului.

Exemplu de declarare event :

// declarare delegate pentru gestionare eveniment click
delegate void ButtonEventHandler(object source,int clickCount);

// declaram evenimentul
public event ButtonEventHandler ButtonClick;


Modelul eveniment al CLR-ului este bazat pe delegates.

Exemplu
Consideram clasa Button pentru care dorim sa tratam mesajul generat de evenimentul clic
pe acesta.


using System;

// declarare delegate pentru gestionare eveniment click
delegate void ButtonEventHandler(object source,int clickCount);

class Button
{
// declaram evenimentul
public event ButtonEventHandler ButtonClick;

//Functia ce genereaza evenimentul
public void Clicked(int count)
{
Console.WriteLine("\nInside Clicked !!!");

//Invocarea tuturor metodelor atasate pentru eveniment
if (ButtonClick != null)
ButtonClick(this,count);
}
}

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 35/54
Asiminoaei Ioan
// In clasa Dialog se foloseste tipul Button
public class Dialog
{
public Dialog()
{
Button b = new Button();

// Adaugam metoda pentru tratatre eveniment
b. ButtonClick += new ButtonEventHandler(
OnButtonAction);
//Generare eveniment
b.Clicked(1);

b.ButtonClick += new ButtonEventHandler(OnButtonAction);
b.Clicked(1);

//Eliminare metoda ce trateaza evenimentul
b.ButtonClick -= new ButtonEventHandler(onButtonAction);
b.Clicked(1);

b.ButtonClick -= new ButtonEventHandler(onButtonAction);
b.Clicked(1);
}

// Clasa de test
static void Main()
{
new Dialog();
}

// Functia (metoda) pentru tratarea evenimentului
public void onButtonAction(object source,int clickCount)
{
Console.WriteLine("Inside Event Handler !!!");
}
}


Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 36/54
Asiminoaei Ioan
Definirea evenimentelor cu MulticastDelegates
Pattern-ul Publish / Subscribe (Event)

Clasa din FCL este MulticastDelegates.
O instanta a acestei clase reprezinta un delegate ce are mai multe elemente in lista de
invocare.

Compilatorul poate deriva din aceasta clasa, dar noi in cod nu putem face explicit acest
lucru. La fel si pentru clasa Delegate.
CLR furnizeaza doua metode speciale: BeginInvoke si EndInvoke folosite in apelul
asincron.
Observatie
Cand este invocat un delegate de acest tip (multicast), delegates din lista de invocare
sunt apelati in mod sincron in ordinea in care apar in lista. Daca apare o eroare in
timpul executiei listei de delegates se genereaza o exceptie.

Aplicatiile Windows au nevoie sa proceseze evenimente in mod asincron. Evenimentele
genereaza mesaje, iar mesajele au atasate functii ce le trateaza.

Modelul delegate multicast are la baza pattern-ul Publish/Subscribe, in care o clasa
publica un eveniment pe care-l poate genera, si un alt numar de clase subscriu pentru
acest eveniment.
Cand evenimentul a fost generat, runtime-ul are grija sa notifice producerea
evenimentului fiecarei clase ce asteapta acel eveniment.

Metoda apelata ca rezultat al producerii unui eveniment este definita de un delegate.

Cand folosim delegate in acest mod, trebuie sa respectam urmatoarele reguli:

1. Delegate trebuie definit ca avand doi parametri.
2. Argumentele reprezinta totdeauna doua obiecte: obiectul ce a generat
evenimentul, si un obiect eveniment.
3. Al doilea parametru trebuie sa fie derivat din clasa EventArgs.





Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 37/54
Asiminoaei Ioan
Descriere pattern Publish / Subscribe sau pattern Event

Modelul logic al patern-ului observer implica doua entitati : Observer si Subiect.
Observer : responsabil pentru afisarea datelor catre utilizator.
Subiect : reprezinta modelul problemei. Cand apare o modificare in Subiect, Observer-ul
observa aceasta modificare si executa actiunea programata .

Modelul fizic

Observer :
Observer-ul isi exprima interesul fata de Subiect pentru anumite notificari, deci acesta se
inregistreaza (Register / Attach) la Subiect cu o anumita cerinta.
Observer-ul trebuie sa aiba si posibilitatea renuntarii de a fi notificat pentru o anumita
cerinta (Unregister / Detach).
Observer-ul instiinteaza Subiect despre metoda ce va trebui apelata cand apare
evenimentul monitorizat (OnNotify).

Subiect :
Inregistreaza / elimina observerii interesati de un anumit eveniment ; uzual metodele
Register / Attach, Unregister / Detach).
Notifica observer despre aparitia evenimentului (Notify).

.NET furnizeaza urmatoarele interfete pentru acest pattern :

1. Interfata IObserver<T>

public interface IObserver<in T>

Interfata IObserver<T> reprezinta clasa ce primeste notificari. T reprezinta clasa ce
furnizeaza informatia de notificare. Furnizorul informatiei este dat de clasa ce
implementeaza interfata IObservable<T>.
Interfata IObserver<T> defineste urmatoarele metode pe care observerul trebuie sa le
implementze :
Metoda OnNext, ce este apelata de provider pentru a furniza informatii catre
observer.
Metoda OnError, apelata de provider pentru a indica ca informatia nu este
disponibila, au aparut anumite erori.
Metoda OnCompleted, apelata de provider pentru a semnala ca a terminat
trimiterea notificarilor la observeri.
public class Observer : IObserver<Tip_clasa_struct>
{
public void OnNext(Tip_clasa_struct value)
{
}
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 38/54
Asiminoaei Ioan

public void OnError(Exception error)
{
}

public void OnCompleted()
{
}
}

2. Interfata IObservable<T> : furnizeaza informatia de notificare

public interface IObservable<out T>

T trebuie sa fie acelasi in IObserver si IObservable.

Provider-ul trebuie sa implementeze o singura metoda, Subscribe ce indica faptul ca un
observer doreste sa fie notificat. Apelantul acestei metode trebuie sa paseze o instanta a
observerului.

Exemplu (MSDN):

public enum LocationStatus { Started = 1, EnRoute = 2, Finished = 3 };

public struct Location
{
public readonly decimal Latitude;
public readonly decimal Longitude;
public readonly DateTime DateAndTime;
public readonly LocationStatus Status;

public Location(decimal lat, decimal lon,
DateTime dateAndTime, LocationStatus status)
{
this.Latitude = lat;
this.Longitude = lon;
this.DateAndTime = dateAndTime;
this.Status = status;
}
}

public class LocationSimulator : IObservable<Location>
{
List<IObserver<Location>> observers =
new List<IObserver<Location>>();
Location _location, _lastLocation, _startLocation;

public static LocationSimulator SetStartingLocation()
{
return new LocationSimulator(42.2857m, -83.7213m,
LocationStatus.Started);
}

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 39/54
Asiminoaei Ioan
private LocationSimulator(decimal latitude,
decimal longitude, LocationStatus status)
{
_location = new Location(latitude, longitude,
DateTime.UtcNow, status);
_lastLocation = _location;
if (status == LocationStatus.Started)
_startLocation = _location;
}

public Location Location
{
get { return this._location; }
}

public Location GetCurrentLocation()
{
Random rnd = new Random();
decimal newLat = _location.Latitude + rnd.Next(-1, 2);
decimal newLong = _location.Longitude + rnd.Next(-1, 2);
// Assume arrival if the difference in latitude is 3.
_lastLocation = _location;
LocationStatus status = Math.Abs(_startLocation.Latitude
newLat) >= 3 ?
LocationStatus.Finished : LocationStatus.EnRoute;
_location = new Location(_location.Latitude + rnd.Next(-1, 2),
_location.Longitude + rnd.Next(-1, 2),
DateTime.UtcNow, status);
if (! _location.Equals(_lastLocation))
{
// Notify observers.
foreach (IObserver<Location> observer in observers)
{
observer.OnNext(this.Location);
// Assume that we've arrived at location of
// Latitude has changed by 4.
if (_location.Status == LocationStatus.Finished)
observer.OnCompleted();
}
}
return this.Location;
}

public IDisposable Subscribe(IObserver<Location> observer)
{
observers.Add(observer);
// Announce current location to new observer.
observer.OnNext(this.Location);
return observer as IDisposable;
}
}

public class LocationDisplay : IObserver<Location>
{
public void OnNext(Location value)
{
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 40/54
Asiminoaei Ioan
Console.WriteLine("{3}At {0}, Latitude = {1:N4},
Longitude = {2:N4}", value.DateAndTime,
value.Latitude, value.Longitude,
value.Status == LocationStatus.Started ? "Starting " : "");
}

public void OnError(Exception error)
{
Console.WriteLine("Unable to determine the current location.");
}

public void OnCompleted()
{
Console.WriteLine("Finished tracking the current location.");
}
}

public class Example
{
public static void Main()
{
LocationSimulator simulator =
LocationSimulator.SetStartingLocation();

// Subscribe with class that implements IObserver<Location>
IDisposable d = simulator.Subscribe(new LocationDisplay());
Location loc;
do {
loc = simulator.GetCurrentLocation();
Thread.Sleep(2500);
} while (loc.Status != LocationStatus.Finished);
}
}


Pattern Observer folosind delegates

Exemplu:

public class Info
{
// delegate pentru eveniment
public delegate void InfoDelegate(object obj);

// event ce foloseste delegate InfoDelegate
public event InfoDelegate InfoChanged;

// data membru
object _obiect;

public object SetInfo
{
set
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 41/54
Asiminoaei Ioan
{
_obiect = value;
// genereaza eveniment
InfoChanged(_obiect);
}
}
}

public class InfoDisplay
{
public void InfoChanged(object obj)
{
Console.WriteLine(Info changed);
}
}

public class Test
{
public static void Main()
{
InfoDisplay infodisplay = new InfoDisplay();

Info info = new Info();

// inregistrare metoda callback pentru event
Info.InfoDelegate del =
new Info.InfoDelegate(InfoDisplay.InfoChanged);
info.InfoChanged += del;

// schimbare stare in Info
info.SetInfo = "changed";

// unregister
info.InfoChanged -= del;
}
}

Pattern Publish / Subscribe sau Event - implementare

Schema generala a cestui pattern este data in urmatorul cod (partial MSDN).

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace EventPattern
{
public class Stoc
{
// declar delegate pt eveniment
public delegate void AskPriceChangedHandler(object sender,
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 42/54
Asiminoaei Ioan
AskPriceChangedEventArgs e);
// declar event
public event AskPriceChangedHandler AskPriceChanged;
// data membru
object _askPrice;
// proprietate
public object AskPrice
{
set
{
_askPrice = value;
// generare event
OnAskPriceChanged();
}
}

// metoda ce genereaza evenimentul
protected void OnAskPriceChanged()
{
AskPriceChanged(this,
new AskPriceChangedEventArgs(_askPrice));
}
} // clasa Stoc

// parametrii evenimentului
public class AskPriceChangedEventArgs : EventArgs
{
private object _askPrice;

// ctor
public AskPriceChangedEventArgs(object askPrice)
{
_askPrice = askPrice;
}

public object AskPrice
{
get
{
return _askPrice;
}
}

} // AskPriceChangedEventArgs

}

iar clasa de test poate fi urmatoarea:

class MainStoc
{
static void Main(string[] args)
{
Stoc s = new Stoc();
s.AskPriceChanged +=new
Stoc.AskPriceChangedHandler(s_AskPriceChanged);
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 43/54
Asiminoaei Ioan
s.AskPrice = 100;

}

static void s_AskPriceChanged(object sender,
AskPriceChangedEventArgs e)
{
Console.WriteLine("Stoc display: {0}", e.AskPrice);
//throw new NotImplementedException();
}
}

Exemplu din Inside C# Second Edition Tom Archer

Observati diferenta la constructori.

Consideram urmatorul exemplu. Dorim sa proiectam o aplicatie pentru e-mail.
Cand soseste un mesaj, utilizatorul doreste ca acesta sa fie redirectionat (forward) catre
un fax sau un pager.

Construim urmatoarele tipuri:
MailManager, ce primeste mesajele e-mail. MailManager va expune un eveniment numit
MailMsg.
Alte tipuri (Fax, Pager) trebuie sa-si manifeste interesul fata de acest eveniment.
Cand se primeste un e-mail, MailManager va produce evenimentul, distribuind mesajul
catre clasele ce s-au inregistrat pentru acest mesaj.

Fiecare obiect proceseaza mesajul dupa cum doreste.

Proiectarea unui tip ce expune un eveniment

Pattern-ul recomandat de Microsoft pentru definirea unui eveniment este urmatorul :

Etapa 0 : Definire tip ce va contine event. (class MailManager).

Etapa 1 : Definirea unui tip imbricat, derivat din EventArgs, tip ce va contine
informatia ce va fi pasata obiectelor interesate de acest eveniment

public class MailMsgEventArgs : EventArgs

Etapa 2 : Definim tipul delegate ce ne va da prototipul metodei callback pe care apelatii vor
trebui sa o implementeze

public delegate void MailMsgEventHandler(
Object sender,
MailMsgEventArgs args);

Etapa 3: Declararea datei membru event
public event MailMsgEventHandler MailMsg;

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 44/54
Asiminoaei Ioan
Etapa 4: Definirea metodei responsabila cu notificarea clientilor despre aparitia
evenimentului

protected virtual void OnMailMsg(MailMsgEventArgs e)

Etapa 5: Definirea metodei ce identifica daca evenimentul a aparut sau nu:

public void SimulateArrivingMsg(
String from,
String to,
String subject,
String body)

Exemplu:

class MailManager
{
// Tipul MailMsgEventArgs este definit in interiorul tipului
// MailManager

public class MailMsgEventArgs : EventArgs
{

public readonly String from, to, subject, body;

// 1. Parametrul evenimentului
public MailMsgEventArgs(
String from, String to, String subject, String
body)
{
this.from = from;
this.to = to;
this.subject = subject;
this.body = body;
}

}

// 2. Tipul delegate prototipul pentru callback
// Metoda trebuie implementata de clienti

public delegate void MailMsgEventHandler(
Object sender, MailMsgEventArgs args);

// 3. Evenimentul
public event MailMsgEventHandler MailMsg;

// 4. Metoda responsabila pentru notificarea obiectelor
// inregistrate pentru acest eveniment

protected virtual void OnMailMsg(MailMsgEventArgs e)
{
if (MailMsg != null)
{
MailMsg(this, e);
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 45/54
Asiminoaei Ioan
}
}

// 5. Metoda este apelata cand apare un mesaj e-mail

public void SimulateArrivingMsg(String from, String to,
String subject, String body)
{
// Construim parametrul evenimentului
MailMsgEventArgs e =
new MailMsgEventArgs(from, to, subject, body);

// Notificam clientul ca a aparut un e-mail

OnMailMsg(e);
}
}

Codul critic se gaseste in clasa MailManager.
Dezvoltatorul trebuie sa defineasca urmatoarele articole:

1. Sa defineasca un tip ce va mentine informatii aditionale ce ar trebui trimise celor ce
asteapata notificarea evenimentului. Tipurile ce mentin informatia despre un eveniment
sunt derivate din System.EventArgs, si numele tipului ar trebui sa se termine cu
EventArgs. In exemplul nostru, tipul MailMsgEventArgs contine campuri ce idetifica cine
a trimis mesajul (from), cine primeste mesajul (to), subiectul mesajului (subject) si corpul
mesajului (body).

EventArgs este definita in FCL astfel:

[Serializable]
public class EventArgs
{
public static readonly EventArgs Empty = new EventArgs();
public EventArgs() { }
}

Acesta serveste ca un tip de baza din care alte tipuri pot deriva.
Exista si evenimente care nu au informatii aditionale de transmis (clic pe un buton).

Cand definim un eveniment ce nu are informatii aditionale de transmis putem folosi
EventArgs.Empty in loc de a construi un nou obiect EventArgs.

2. Definim un tip delegate, specificand prototipul metodei ce va fi apelata cand se
genereaza evenimentul. Prin conventie numele delegate se termina cu EventHandler.
Prototipul trebuie sa returneze void si sa aiba doi parametri. Primul parametru este un
Object ce se refera la la obiectul ce a trimis notificarea, iar al doilea parametru este un
tip derivat din EventArgs ce contine informatii aditionale despre notificare.

Prototipul pentru EventHandler este urmatorul:

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 46/54
Asiminoaei Ioan
public delegate void EventHandler(Object sender, EventArgs e);

3. Definim un eveniment, MailMsg, de tip MailMsgEventHandler, deci metoda callback
trebuie sa aiba acest prototip.

4. Definim o metoda virtuala, protected, metoda responsabila pentru notificarea
obiectelor inregistrate sa primeasca acest eveniment. Metoda OnMailMsg este apelata
cand un nou mesaj soseste.
Aceasta metoda primeste un obiect MailMsgEventArgs initializat ce contine informatii
aditionale despre eveniment.
Aceasta metoda verifica daca exista cineva interesat de acest eveniment pentru a da
drumul evenimentului.
Un tip ce foloseste MailManager ca tip de baza poate suprascrie metoda OnMailMsg.

5. Definim o metoda ce transforma intrarea in evenimentul dorit.
In exemplul nostru metoda SimulatingArrivingMsg este apelata pentru a indica ca un
mesaj nou a sosit in MailManager.

6. Sa examinam indeaproape ce inseamna sa definim un event MailMsg.

public event MailMsgEventHandler MailMsg;

Compilatorul C# transforma aceasta linie de cod in:

// 1. Referinta la inceputul listei de delegates

private MailMsgEventHandler MailMsg = null;

// 2. A PUBLIC add_* method
// Allows objects to register interest in the event.

[MethodImplAttribute(MethodImplOptions.Synchronized)]
public void add_MailMsg(MailMsgEventHandler handler)
{
MailMsg = (MailMsgEventHandler)Delegate.Combine(
MailMsg, handler);
}

// 3. A PUBLIC remove_* method
// Allows objects to unregister interest in the event.
[MethodImplAttribute(MethodImplOptions.Synchronized)]
public void remove_MailMsg(MailMsgEventHandler handler)
{
MailMsg = (MailMsgEventHandler)
Delegate.Remove(MailMsg, handler);
}

Cand un obiect este interesat de eveniment, acest camp referentiaza o instanta a delegate
MailMsgEventHandler.
Fiecare instanta delegate MailMsgEventHandler are un pointer la un alt delegate
MailMsgEventHandler sau la null pentru a marca sfarsitul listei.
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 47/54
Asiminoaei Ioan

Proiectarea unui tip ce asteapta pentru un eveniment

Exemplu

class Fax
{
// Pass the MailManager object to the constructor.
public Fax(MailManager mm)
{
// Indicam metoda ce va fi apelata cand soseste un e-mail
mm.MailMsg+= new MailManager.MailMsgEventHandler(FaxMsg);
}

// Aceasta este metoda pe care MailManager o va apela pentru
// a notifica obiectul Fax ca a sosit un nou e-mail.

private void FaxMsg(
Object sender, MailManager.MailMsgEventArgs e)
{
// sender identifica MailManager

Console.WriteLine("Faxing mail message:");
Console.WriteLine(
" To: {0}\n From: {1}\n Subject: {2}\n Body: {3}\n",
e.from, e.to, e.subject, e.body);
}

public void Unregister(MailManager mm)
{
// Construct an instance of the MailMsgEventHandler
// delegate that refers to the FaxMsg callback method.

MailManager.MailMsgEventHandler callback =
new MailManager.MailMsgEventHandler(FaxMsg);

// Unregister with MailManagers MailMsg event.

mm.MailMsg -= callback;
}
}

Cand aplicatia de e-mail se initializeaza, va construi mai intai un obiect MailManager si
va salva referinta la acest obiect intr-o variabila.
In continuare se construieste un obiect de tip Fax, pasindu-i referinta la obiectul
MailManager.

In ctor Fax se construieste un nou obiect delegate MailManager.MailMsgEventHandler
care este un wrapper pentru metoda FaxMsg din Fax ce are acelasi prototip ca
MailMsgEventHandler (altfel codul nu se compileaza).
In continuare obiectul Fax se inregistreaza pentru evenimentul MailMsg din
mailManager :

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 48/54
Asiminoaei Ioan
mm.MailMsg += new MailManager.MailMsgEventHandler(FaxMsg);

Compilatorul C# va transforma operatorul += in urmatoarea linie de cod pentru a adauga
interesul obiectului pentru eveniment :

mm.add_MailMsg(new MailManager.MailMsgEventHandler(FaxMsg));

Aceasta metoda o putem apela si explicit.

Cand MailManager produce evenimentul, obiectul Fax va apela metoda FaxMsg ce
primeste o referinta la un obiect MailManager si o referinta la MailMsgEventArgs.
Referinta la MailManager poate fi folosita pentru a accesa campuri sau metode din
MailManager.
Din obiectul MailMsgEventArgs, metoda FaxMsg are acces la toate caracteristicile
mesajului.


Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 49/54
Asiminoaei Ioan
Delegates asincroni

Apelul asincron al delegates presupune crearea de catre framework a unui fir de lucru din
ThreadPool. Din acest motiv apelul nu este blocant.

Este posibil ca metoda apelata sa genereze date si sa le returneze. In acest caz exista doua
posibilitati oferite de infrastructura .NET :
apelantul asteapta explicit aceste date apel metoda EndInvoke ;
apelantul este instiintat de apelat ca executia s-a terminat .


Clasa System.MulticastDelegates contine printre altele si metodele

IAsyncResult BeginInvoke(...,
AsyncCallback asc,
object stateObject);
EndInvoke(..., IAsyncResult);

folosite in apelul asincron al delegates-lor.

Observam ca aceste metode returneaza sau au ca parametru un tip IAsyncResult, care
e definit ca o interfata.

IAsyncResult reprezinta statusul unei operatii asincrone.
IAsyncResult este implementata de clase ce contin metode ce pot opera in mod
asincron.
IAsyncResult este tipul returnat de o metoda ce initiaza o operatie asincrona si
este un parametru al metodei ce verifica terminarea operatiei asincrone (in mod
normal metode ce incep cu Begin... si End...).

Un obiect de tip IAsyncResult este pasat metodelor invocate de delegates
AsyncCallback cand o operatie asincrona s-a terminat de executat.
Un obiect ce suporta interfata IAsyncResult memoreaza informatie de stare
pentru o operatie asincrona si furnizeaza un obiect de sincronizare ce permite
firelor sa fie semnalate cand operatia s-a terminat.
Clasa AsyncResult este implementarea interfetei IAsyncResult ce este returnata de
metoda BeginInvoke cand folosim un delegate pentru a apela o metoda in mod
asincron.

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 50/54
Asiminoaei Ioan

public interface IAsyncResult
{
// Furnizeaza un obiect definit de utilizator
//sau contine informatii despre o operatie asincrona

object AsyncState { get; }

// Returneaza un WaitHandle folosit pentru a astepta
// terminarea unei operatii asincrone
// Valoarea returnata poate fi folosita pentru
// a executa operatii WaitOne, WaitAny sau WaitAll

WaitHandle AsyncWaitHandle { get; }

// Returneaza o valoare ce indica daca operatia
// asincrona s-a terminat in mod sincron

bool CompletedSynchronously { get; }

// Returneaza o valoare ce indica daca operatia
//asincrona a fost terminata

bool IsCompleted { get; }
}

Clasa AsyncResult
Incapsuleaza rezultatele unei operatiuni asincrone pe un delegate.
Namespace: System.Runtime.Remoting.Messaging
Assembly: mscorlib (in mscorlib.dll)

[ComVisibleAttribute(true)]
public class AsyncResult : IAsyncResult, IMessageSink

Observatie
IAsyncResult returnat de metoda BeginInvoke poate fi convertit (cast) la
AsyncResult. AsyncResult are proprietatea AsyncDelegate ce mentine obiectul
delegate pe care a fost facut apelul asincron.
Metoda BeginInvoke este folosita pentru a initia un apel asincron. Are aceeasi
parametri ca metoda pe care dorim sa o executam asincron, plus alti doi parametri ce
specifica metoda ce se va apela cand se termina BeginInvoke si un obiect ce transmite
informatii aditionale.
EndInvoke (metoda blocanta) este folosita pentru a regasi rezultatele unui apel asincron.
Poate fi apelata dupa apelul lui BeginInvoke.
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 51/54
Asiminoaei Ioan
Dup ace am apelat BeginInvoke putem:
Sa executam alt cod si apoi sa apelam EndInvoke pentru a obtine rezultatul.
Sa obtinem un Waithandle folosind IAsyncResult.AsyncWaitHandle, si sa
folosim metoda WaitOne pentru a bloca executia pana cand WaitHandle este
semnalat, si apoi sa apelam EndInvoke.
Sa indicam o metoda callback (in BeginInvoke), metoda ce va fi apelata cand se
termina executia lui BeginInvoke, si putem apela EndInvoke.
Exemplu

1. Instantiem un delegate ce contine metoda pe care dorim sa o rulam in paralel (in mod
obisnuit unul din delegates predefiniti Func).
2. Apelam BeginInvoke pe delegate, salvand valoarea de retur de tip IAsyncResult.
BeginInvoke returneaza imediat catre apelant, deci putem executa alt cod in continuare.
3. Cand avem nevoie de rezultat, apelam EndInvoke pe delegate, pasand ca valoare
obiectul de tip IAsyncResult, salvat anterior.

static void Main()
{
// string tipul parametrului din metoda Work
// int tipul returnat de metoda Work

Func<string, int> method = Work;
IAsyncResult cookie = method.BeginInvoke ("test",
null, null);

//
// ... cod ce poate fi executat dupa apelul BeginInvoke
//

int result = method.EndInvoke (cookie);
Console.WriteLine ("String length is: " + result);
}

static int Work (string s)
{
return s.Length;
}

Cazul cand penultimul parametru din BeginInvoke este completat, se indica metoda ce
va fi apelata cand s-au terminat calculele.

static void Main()
{
Func<string, int> metoda = Work;
method.BeginInvoke ("test", Done, metoda);
// ...
//
Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 52/54
Asiminoaei Ioan
}
static int Work (string s) { return s.Length; }

static void Done (IAsyncResult cookie)
{
// ultimul parametru din BeginInvoke este de tip delegate

var target = (Func<string, int>) cookie.AsyncState;
int result = target.EndInvoke (cookie);
Console.WriteLine ("String length is: " + result);
}

Varianta cand obtinem delegate din AsyncResult (namespace
System.Runtime.Remoting.Messaging)

Cod in Main

// delegate de forma
Func<int,int,int> add = Add;
IAsyncResult iasr = add.BeginInvoke(20, 30, Done, "Gata");

// alta varianta

// IAsyncResult iasr = add.BeginInvoke(20, 30,
// new AsyncCallback(Done),, "Gata");


Metoda Add

static int Add(int x, int y)
{
Console.WriteLine("\nAdd runing...\n");
return x + y;
}

Metoda Done

static void Done (IAsyncResult cookie)
{
// obtinem delegate pentru a putea apela EndInvoke
AsyncResult ar = (AsyncResult)cookie;
var target = (Func<int,int,int>)ar.AsyncDelegate;

int result = target.EndInvoke (cookie);

// parametrul pasat metodei Done
string dummy = (string)cookie.AsyncState;
Console.WriteLine ("Done apelata cu {0} si +
result este = {1} ", dummy, result);
}

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 53/54
Asiminoaei Ioan
Putem sa ne imaginam urmatoarea schema pentru apelul asincron al delegates (Richard
Grimes):



Numerele din paranteze arata ordinea de executie data de framework.

Sa examinam urmatorul cod :

public delegate int CalledDelegate(ref int i);

public class Called
{
public int Time2(ref int i)
{
i = i * 2;
return i;
}
}

in clasa de test scriem urmatorul cod :

Console.WriteLine("\nApel asincron delegates\n");

Called c = new Called();
CalledDelegate d;

Apelantul (fir apelant)
Called c = new Called();
CalledDelegate d;
d = new CalledDelegate(c.time2);
IAsyncResult ar;
(1) ar = d.BeginInvoke(42, null, null);
// cod
(3) ar.AsyncWaitHandle.WaitOne();
(5) int res = d.EndInvoke();
CalledDelegate
Metoda = Called.time2;
Target = c;
Rezultat din metoda
Called
int time2(int i)
{ return i *2;}

AsyncThread
2
4
AsyncResult
AsyncWaitHandle

Delegates
Facultatea de Informatica Iasi Universitatea Al I. Cuza Iasi 04.11.2013 54/54
Asiminoaei Ioan
// atasare metoda callback

d = new CalledDelegate(c.Time2);
IAsyncResult ar;
int n = 42;

// se creaza firul si obiectul de sincronizare
// se lanseaza firul

ar = d.BeginInvoke(ref n, null, null);

// cod ce se executa in paralel cu delegate asincron

Console.WriteLine("cod n = {0}", n);

// asteptare terminare delegate

ar.AsyncWaitHandle.WaitOne();

// obtinere rezultat

int res = d.EndInvoke(ref n,ar);

// afisare rezultat

Console.WriteLine("Rezultat : {0} si n = {1}",
res, n);

Rezultatul este (observati valoarea lui n nu e modificata inca in instructiunea) :

Console.WriteLine("cod n = {0}", n);

Apel asincron delegates

cod n = 42
Rezultat : 84 si n = 84
Press any key to continue . . .

You might also like