C#Programmazione

Delegate

I delegate sono un tipo di riferimento che rappresenta un metodo, trattarli come dati e quindi assegnarli ad una variabile o passarli come argomento ad un’altro metodo. Esempio di definizione di un delegate, notiamo che non ha un corpo :

[modificatore] delegate tipo_ritorno NomeDelegate([parametri]);

Supponiamo di voler creare un delegate che riceve un intero e restituisce una stringa, utilizziamo un metodo che lo incapsula:

A questo punto possiamo istanziarlo:

Int2StringDelegate isdel = converter.ConvertToString;

Ora possiamo invocarlo semplicemente:

string str = isdel(numero);

Oppure usarlo come parametro di un metodo:

Un delegate può mantenere al suo interno più riferimenti a diversi metodi, questo è utile per scrivere codice che possa notificare più oggetti invocando di ognuno un metodo.

Invocando il delegate i metodi verranno eseguiti nell’ordine di inserimento.

Per aggiungere un delegate possiamo utilizzare gli operatori + e +=, se invece vogliamo rimuoverlo utilizziamo gli operatori – e -=. L’aggiunta o la rimozione crea un nuovo delegate, i parametri in ingresso possono subire delle modifche ma il metodo successivo utilizza sempre il valore iniziale tranne che usiamo il modificatore ref e quindi il valore modificato da un metodo viene passato al metodo successivo. In un delegate multicast il parametro in uscita utilizzabile è solo l’ultimo gli altri verranno ignorati.

Il passo successivo sono i delegate generici, ecco un esempio

public delegate TDest ConvToDest<TDest, UOrig>(UOrig val);

Possiamo utilizzarlo ad esempio in due versioni differenti:

ConvToDest<string, int> convStr = IntToString;
string s = convStr(123);
ConvToDest<int, string> convInt = StringToInt;
int i = convInt(“123”);

delegate generici Func
Accettano un massimo di 16 parametri e restituiscono un risultato. Supponiamo di scrivereun metodo per il calcolo del fattoriale

Scriviamo un metodo generico di calcolo che utilizza il delegate Func

public T Evaluate(Func<T, T> func, T val)
{ return func(val); }

Possiamo utilizzare il metodo generico evaluate per calcolare il fattoriale nel seguente modo

Func<int, int> fFatt = Fattoriale;
int result = program.Evaluate(fFatt,5);

Possiamo utilizzare lo stesso metodo Evaluate per il calcolo della radice quadrata

Func<double, double> fRadice = Math.Sqrt;
double result = program.Evaluate(fRadice, 144);

delegate generici Action
Semplicemente accetta parametri in ingresso, esegue un’azione ma non restituisce nessun valore.
Ad esempio ForEach(Action<T>)  esegue  un’azione, se T e’ una lista List<T> l’azione viene eseguita su tutti gli elementi della lista.

Vi è un particolare delegate Predicate<T>, valuta i criteri sull’argomento passato e restituisce un valore booleano. Ad esempio

lista.FindAll(Predicate<T>);

Se Predicate è un metodo che verifica se un numero della lista è pari possiamo selezionare da una lista di numeri solo quelli pari. Quando il metodo è utilizzato solo dentro il delegate possiamo evitare di creare un metodo ma inserirlo in linea con il delegate, questo esempio estrae solo i numeri pari

List<int> lPari = lista.FindAll(delegate(int i) { return i % 2 == 0; });

Le espressioni lambda vengono spesso usate per istanziare un delegate, la forma è la seguente:

(parametri) => espressione

Supponiamo che vogliamo calcolare la media di due numeri e un’Action che legge un numero e lo visualizza possiamo semplicemente scrivere

Func<int, int, double> calcMedia = (x, y) => (x + y) / 2.0;
Action<double> stampaMedia = (num) => Console.WriteLine($”la media è: ” {num.ToString()}”);

Vediamo ora come utilizzare i delegate nella gestione degli eventi.
Gli eventi consentono ad un oggetto (publisher) di avvisare altri oggetti (subscriber) che si è verificato qualcosa, il publisher non sa quali subscriber saranno i consumatori dell’evento. Il delegate funge da intermediario fra le due parti.

La prima cosa da fare è una classe event che conterrà le informazioni relative all’evento, come esempio definiamo un evento quando il motore si spegne

public event EventHandler MotoreSpento;

Ora bisogna definire quando generare l’evento e avvisare altri oggetti interessati all’evento

MotoreSpento(this, EventArgs.Empty);

Infine bisogna notificare che un oggetto è interessato all’evento e cosa fare quando viene generato

car.MotoreSpento += GestisciMotoreSpento;

public void GestisciMotoreSpento(object sender, EventArgs args)
{ … }

Supponiamo ora di voler passare al metodo che gestisce l’evento alcune informazioni, ad esempio il numero di giri raggiunto

Scriviamo l’evento MotoreFuoriGiri che deve visualizzare il numero di giri raggiunto

public event EventHandler MotoreFuoriGiri;

MotoreFuoriGiri(this, new GiriMotoreEventArgs() { NumeroGiriRaggiunto = this.numeroGiri });

car.MotoreFuoriGiri += GestisciMotoreFuoriGiri;

public void GestisciMotoreFuoriGiri(object sender, GiriMotoreEventArgs args)
{ … }

Quando non interessa più gestire l’evento basta rimuoverlo dalla lista

car.MotoreFuoriGiri -= GestisciMotoreFuoriGiri;