L'uso dei delegates con ASP.NET

3 pagine in totale: <<Indietro 1 2 [3]

uc.AZHandler+=new AZHandler1(this.SetText)

Nel secondo esempio, nella pagina principale si è utilizzata questa istruzione per instanziare un delegate. Per spiegare questa "strana" sintassi prendiamo ad esempio una semplicissima console application che richiama una funzione statica con i delegate:

using System;

namespace ConsoleApplication1
{
  public delegate void AZHandler(DateTime ora);

  public class Classe2
  {
   public static void ScriviData(DateTime adesso)
   {
     Console.WriteLine("Data attuale: "+adesso.ToString());
   }
  }

  class Class1
  {
   static void Main(string[] args)
   {
     AZHandler az=new AZHandler(Classe2.ScriviData);
     az(DateTime.Now);
     Console.ReadLine();
   }
  }
}

Una volta eseguito, questo codice visualizza l'ora attuale del sistema. Anche se questo codice potrebbe risultare assurdo visto che il tutto si poteva risolvere con molte linee di codice, esso ha il vantaggio di mostrare chiaramente l'uso dei delegate.

Abbiamo dichiarato inizialmente un delegate pubblico:

public delegate void AZHandler(DateTime ora)
. A differenza della dichiarazione di variabili, essa non crea e/o occupa zone di memoria per la memorizzazione dei nostri dati, ma dichiara solo che nella nostra applicazione vogliamo utilizzare un delegate per richiamare una funzione che accetterà un solo parametro DateTime e non restituirà alcun dato, essendo di tipo void.

La classe "Classe2" contiene la funzione statica "ScriviData", di tipo void che accetta solo un parametro DateTime. Nella classe principale istanziamo in "az" il delegate AZHandler (nome di fantasia, possiamo utilizzare quello che vogliamo nei nostri esempi, ma è consigliato inserire come parte finale del nome sempre Handler), e gli diamo come parametro la classe e il nome della funzione che vogliamo richiamare dinamicamente.

Infine con

az(DateTime.Now);
ci limitiamo a chiamare la funzione "Classe2.ScriviData" come deciso nella linea di codice precedente, passando come parametro la data attuale. Semplice, vero?

Multicast delegate

Nell'esempio degli user control era presente una sintassi simile, con l'operatore "+" per instanziare il delegate. La potenza di questa tecnica sta nella possibilità di mantenere una lista di funzioni associate per ogni istanza di delegate. Questa feature ? che viene chiamata multicast delegate ? ci permette di richiamare con un singolo delegate più funzioni contemporaneamente.

Riprendiamo l'ultimo esempio della console application ed aggiungiamo una nuova classe con una funzione statica, modificando il codice della classe principale in questo modo:

public class Classe3
  {
   public static void ScriviSoloData(DateTime adesso)
   {
     Console.WriteLine("Data attuale: "+adesso.ToShortDateString());
   }
  }

  class Class1
  {
   static void Main(string[] args)
   {
     AZHandler az=new AZHandler(Classe2.ScriviData);
     az+=new AZHandler(Classe3.ScriviSoloData);
     az(DateTime.Now);
     Console.ReadLine();
   }
  }

Ora saranno richiamate entrambe le funzioni, visualizzando sia la data completa che parziale. Nessuno ci vieta di inserire tutte le funzioni che desideriamo, naturalmente devono avere tutte la stessa "signature" che abbiamo dichiarato nel delegate iniziale.

// Sarà richiamata una volta la funzione "Classe2.ScriviData" e
// cinque volte "Classe3.ScriviSoloData".
AZHandler az=new AZHandler(Classe2.ScriviData);
az+=new AZHandler(Classe3.ScriviSoloData);
az+=new AZHandler(Classe3.ScriviSoloData);
az+=new AZHandler(Classe3.ScriviSoloData);
az+=new AZHandler(Classe3.ScriviSoloData);
az+=new AZHandler(Classe3.ScriviSoloData);
az(DateTime.Now);

Così come possiamo aggiungere tutte le funzioni desiderate, possiamo anche togliere quelle che in quel contesto non ci interessano:

AZHandler az=new AZHandler(Classe2.ScriviData);
az+=new AZHandler(Classe3.ScriviSoloData);
az(DateTime.Now);
az-=new AZHandler(Classe3.ScriviSoloData);
az(DateTime.Now);

Il primo

az(DateTime.Now)
richiamerà entrambe le funzioni, il secondo richiamerà solo la prima funzione visto che "Classe3.ScriviSoloData" è stato cancellata dalla lista delle funzioni del delegate.

Se volessimo controllare maggiormente l'esecuzione delle singole funzioni presenti nel delegate, potremmo "iterare" all'interno della lista di ogni singolo delegate. Per esempio, riprendendo l'esempio del delegate multicast visto poco fa:

?
AZHandler az=new AZHandler(Classe2.ScriviData);
az+=new AZHandler(Classe3.ScriviSoloData);
az+=new AZHandler(Classe3.ScriviSoloData);
az+=new AZHandler(Classe3.ScriviSoloData);
az+=new AZHandler(Classe3.ScriviSoloData);
az+=new AZHandler(Classe3.ScriviSoloData);
?

Per sapere quante sono le funzioni inserite:

Delegate[] lista=az.GetInvocationList();
Console.WriteLine("Numero di funzioni: {0}",lista.Length);

E se volessimo controllare direttamente l'esecuzione di ogni singola funzione:

Delegate[] lista=az.GetInvocationList();
foreach (Delegate d in lista)
{
  // Richiama a una a una tutte le funzioni presenti nel delegate
  d.DynamicInvoke(new  object[] {DateTime.Now});
  // Se la funzione "ScriviData" accettasse più parametri,
  // li dovremo aggiungere nell'array in questo modo:
  // new object[] {DateTime.Now,strina,numero};
}

In ultimo, è possibile richiamare il metodo del delegate in modalità asincrona (anche se questo ci preclude la strada dell'uso del delegate multicast):

public class Classe2
{
  public static void ScriviData(DateTime adesso)
  {
   Console.WriteLine("Data attuale: "+adesso.ToString());
  }
  public static void FineCallBack(IAsyncResult ar)
  {
   Console.WriteLine("Fine!");
  }
}
Public class Class1
{
  static void Main(string[] args)
  {
   AZHandler az=new AZHandler(Classe2.ScriviData);
   az+=new AZHandler(Classe2.ScriviData);
   AsyncCallback ac=new AsyncCallback(Classe2.FineCallBack);
   IAsyncResult ar=az.BeginInvoke(DateTime.Now,ac,null);
   Console.ReadLine();
  }
}

Per far questo abbiamo utilizzando la classe AsyncCallBack e l'interfaccia IAsyncResult. Tra i parametri che dobbiamo inserire nel metodo BeginInvoke, oltre a quelli della funzione richiamata dal nostro delegate, possiamo inserire anche un'eventuale funzione da richiamare alla fine della funzione principale ? in questo caso "FineCallBack" che visualizza il messaggio "Fine".

Event

Il lettore attento avrà notato che la sintassi per l'uso del delegate non è nuova. Dando uno sguardo alla righe di codice che VS.NET aggiunge in automatico, possiamo notare che le dichiarazione degli eventi dei controls presenti nella pagina hanno una sintassi simile a quella usata con i delegate. La risposta a questa curiosità è semplice: gli event sono, a tutti gli effetti, dei delegate! Le uniche differenze sono alcune limitazioni delle stesse. Gli eventi hanno questi "obblighi":

  • possonoessere solo di tipo void.

  • accettano solo due parametri: l'oggetto generico object e la classe EventArgs.

Nel primo parametro dev'essere inserito l'oggetto che ha fatto "scattare" l'evento, nel secondo l'istanza di una classe EventArgs o una classe ereditata da essa. Riproviamo a scrivere l'esempio della console application con gli event, ma questa volta la funzione che sarà avviata dal delegate è presente all'interno della classe principale e sarà richiamata da una classe secondaria:

using System;
namespace ConsoleApplication1
{
  public delegate void AZEventHandler(object o, AZEventArgs aze);
  public class AZEventArgs:EventArgs
  {
   private DateTime _ora;
   public DateTime Adesso
   {
     set {_ora=value;}
     get {return _ora;}
   }
  }

  class Classe3
  {
   public event AZEventHandler Evento;
   public void AvviaEvento()
   {
     AZEventArgs parametri=new AZEventArgs();
     parametri.Adesso=DateTime.Now;
     if (Evento!=null)
      Evento(this,parametri);
   }
  }

  class Class1
  {
   public static void ScriviData(object o, AZEventArgs aze)
   {
     Console.WriteLine("Data attuale: "+aze.Adesso.ToString());
   }
   static void Main(string[] args)
   {
     Classe3 nc3=new Classe3();
     nc3.Evento+=new AZEventHandler(Classe2.ScriviData);
     nc3.AvviaEvento();
     Console.ReadLine();
   }
  }
}

Inizialmente abbiamo creato una classe EventArgs personalizzata in cui abbiamo aggiunto la proprietà "Adesso" in cui inseriremo e leggeremo la data attuale. Quindi abbiamo inserito in "Class1" che, ricordo, è quella che sarà eseguita all'avvio dell'applicazione, la funzione "ScriviData" che visualizzerà la data e l'ora del sistema. "Classe2" è la classe che avvierà l'evento. In questo caso per avviare l'evento sarà sufficiente richiamare la funzione statica "AvviaEvento" che creerà la classe EventArgs personalizzata, quindi richiamerà la funzione "agganciata" all'evento. Naturalmente anche in questo caso è possibile aggiungere più funzioni.

Conclusioni

Con questo articolo ho voluto spiegare solo i concetti necessari per la comprensione e l'uso di delegate ed eventi. E' possibile scaricare l'esempio di una web application composta di cinque pagine, le prime quattro che utilizzano i delegate per richiamare delle funzioni da alcuni user control, l'ultimo esempio, il più complesso, utilizza gli event con inserimento dinamico di uno user control all'interno di un Datalist.

Buon divertimento!

3 pagine in totale: <<Indietro 1 2 [3]

Attenzione: Questo articolo contiene un allegato

Contenuti dell'articolo

Commenti
Dai un voto a questo articolo, ci aiuterà a migliorare il nostro sito (1 è il voto minimo, 5 il massimo).

Per procedere al rating dell'articolo devi essere autenticato.

Aggiungi un nuovo commento »»»
Per inserire un commento, devi registrarti alla nostra community.


TUTORIALS
TOP TEN ARTICOLI
NOTIFICHE

Iscriviti alla nostra newsletter nuoviarticoli per ricevere e-mail le notifiche!

Indirizzo e-mail:
PROVIDER ASP.NET 2.0

Seleziona il database per avere il web.config pronto per Membership, Roles e Profile API.



IN EVIDENZA
MISC