Pagina personale di:
Carlo Vecchio
appunti di C#, R, SQL Server, ASP.NET, algoritmi, numeri
Vai ai contenuti

C# - Delegati

C#

Definizione ed esempio

  • Un "delegato" (in inglese "delegate") è un puntatore a una funzione o se si preferisce un oggetto che sa come chiamare un metodo. In altre parole un delegato definisce (a) il tipo di ritorno e (b) i tipi dei parametri di metodi.
  • Per esempio, definiamo:

   delegate int Operazione(int primoOperando, int secondoOperando);

  • Questo delegato è compatibile con qualsiasi metodo che accetta due parametri int e restituisce un int.
  • Definiamo due metodi compatibili con il delegato:

   // Un metodo
   
static int Somma(int addendo1, int addendo2)
   {
       
return addendo1 + addendo2;
   }
       
   
// Un altro metodo
   
static int Prodotto(int fattore1, int fattore2)
   {
       
return fattore1 * fattore2;
   }

  • Ora utilizziamo il delegato:

   // Creazione dell'istanza del delegato
   
Operazione op;

   
// L'istanza del delegato esegue la Somma
   op =
new Operazione(Somma);
   
Console.WriteLine(op.Invoke(4, 2));

   
// L'istanza del delegato esegue il prodotto
   op =
new Operazione(Prodotto);
   
Console.WriteLine(op.Invoke(4, 2));

  • Si può utilizzare anche una sintassi più breve:

   op = Somma;
   Console
.WriteLine(op(4, 2));

  • Quindi tramite una istanza di un delegato è possibile chiamare metodi diversi (purché accettino gli stessi tipi nei parametri e restituiscano lo stesso tipo).
 
Un esempio console
   static void Main(string[] args)
   {
   
   Logger myLogger1 = new Logger(ConsoleWriter1);
       myLogger1.Log("Hello world!");

   
   Logger myLogger2 = new Logger(ConsoleWriter2);
       myLogger2.Log("Hello world!");

   
   Console.ReadKey();
   }

   // Dichiarazione del delegate
   public delegate void StringLogWriter(DateTime timeStamp, string message);

   // Prima implementazione del delegate
   private static void ConsoleWriter1(DateTime timeStamp, string message)
   {
       Console.WriteLine("{0}, {1}", timeStamp, message);
   }

   // Seconda implementazione del delegate
   private static void ConsoleWriter2(DateTime timeStamp, string message)
   {
       Console.WriteLine("Timestamp: {0}, Message: {1}", timeStamp, message);
   }

  • La classe seguente, nel costruttore ha un parametro che è un oggetto di tipo delegato. Lo memorizza al suo interno e poi offre un metodo che prende come parametro il delegato stesso. L’esecuzione del metodo richiama quindi l’esecuzione di una delle possibili implementazioni del delegato.
  • Il codice eseguito sarà quindi uno dei codici possibili delle varie implementazioni del delegato e dipende da come è stato istanziato l’oggetto dalla classe.

   // Classe che utilizza il delegate
   
public class Logger
   {
       private StringLogWriter _writer;

       public Logger(StringLogWriter writer)
       {
           _writer = writer;
       }

   
   public void Log(string message)
       {
           if (_writer != null)
               _writer(DateTime.Now, message);
       }
   }


Esempio di estrazione oggetti da una lista

  • Supponiamo di avere una classe e una lista di oggetti di questa classe. Supponiamo inoltre che la classe abbia un metodo che esegue una qualche operazione su alcuni oggetti, non su tutti (per esempio una visualizzazione, oppure un update su un database). L’operazione deve essere eseguita solo sotto certe condizioni e le condizioni possono essere diverse in base ad altri parametri.
  • Una soluzione potrebbe essere quella di scrivere più metodi nella classe, ognuno con una specifica logica.
  • Invece con i delegati possiamo scrivere un unico metodo che esegue l’operazione e lasciare a una funzione delegata il compito di decidere se il record è tra quelli da elaborare oppure no. Se poi la funzione delegata la scriviamo con una espressione lambda, il codice diventa molto compatto.
  • Come classe, definiamo la classe ‘Persona’, con le proprietà ‘Nome’, ‘Cognome’, ‘DataNascita’, ‘Altezza’, ‘Peso’ e con il metodo statico generico ‘EsegueQualcosa()’. Questo metodo lavora con la lista (primo argomento) ma come secondo parametro ha un delegato.

   class Persona
   {
       public string Nome { get; set; }
       public string Cognome { get; set; }
       public DateTime DataNascita { get; set; }
       public int Altezza { get; set; }
       public int Peso { get; set; }

       public static void EsegueQualcosa(List<Persona> listaPersone, PersonaSelezionata pSelezionata)
       {
           foreach (Persona p in listaPersone)
           {
               if (pSelezionata(p))
               {
                   // Esegue qualcosa
                   Console.WriteLine("Nome: " + p.Nome + " Cognome: " + p.Cognome);
               }
           }
       }
   }

  • Definiamo un delegato che restituisce un bool e ha come argomento un oggetto di tipo ‘Persona’.

   delegate bool PersonaSelezionata(Persona p);

  • Definiamo una lista di oggetti di tipo ‘Persona’ e aggiungiamo alcuni elementi.
 
   List<Persona> listaPersone = new List<Persona>();
   listaPersone.Add(new Persona() { Nome = "Leonardo", Cognome = "Da Vinci",
                    DataNascita = new DateTime(1452, 4, 15), Altezza = 170, Peso = 68 });
   listaPersone.Add(new Persona() { Nome = "Michelangelo", Cognome = "Buonarroti",
                    DataNascita = new DateTime(1475, 3, 6), Altezza = 175, Peso = 72 });
   listaPersone.Add(new Persona() { Nome = "Tiziano", Cognome = "Vecellio",
                    DataNascita = new DateTime(1576, 8, 27), Altezza = 172, Peso = 75 });
   listaPersone.Add(new Persona() { Nome = "Galileo", Cognome = "Galilei",
                    DataNascita = new DateTime(1642, 8, 1), Altezza = 168, Peso = 80 });
   listaPersone.Add(new Persona() { Nome = "Giovanni", Cognome = "Keplero",
                    DataNascita = new DateTime(1571, 12, 27), Altezza = 175, Peso = 82 });

  • Infine, la selezione degli oggetti avviene chiamando il metodo ‘EsegueQualcosa()’ con le opportune espressioni lambda.

   // Seleziona le persone con data di nascita > del 01/01/1500
   Persona.EsegueQualcosa(listaPersone, p => p.DataNascita > new DateTime(1500, 1, 1));

  // Seleziona le persone con data di nascita > del 01/01/1500 e peso <= 80
   Persona.EsegueQualcosa(listaPersone, p => p.DataNascita > new DateTime(1500, 1, 1) && p.Peso <= 80);

Delegati Multicast
  • Un Delegato Multicast è un delegato che richiama più delegati.
  • All’istanza del delegato multicast, possono essere aggiunti o rimossi i delegati con gli operatori '+' e '-'. I delegati aggiunti, sono poi eseguiti nello stesso ordine con il quale sono stati aggiunti. Se essi hanno un valore di ritorno, il delegato multicast ritorna il valore dell’ultimo delegato della lista.
  • Ecco un semplice esempio.

   // Si dichiara il delegato.
   delegate void WriteLineInConsole();

   // Si definiscono dei metodi.
   private void WriteLineA()
   {
       Console.WriteLine("A");
   }

   private void WriteLineB()
   {
       Console.WriteLine("B");
   }

   private void WriteLineC()
   {
       Console.WriteLine("C");
   }

   // Si istanziano i delegati.
   WriteLineInConsole a = new WriteLineInConsole(WriteLineA);
   WriteLineInConsole b = new WriteLineInConsole(WriteLineB);
   WriteLineInConsole c = new WriteLineInConsole(WriteLineC);
   WriteLineInConsole tot = a + b + c;

   // Si invoca il delegato multicast.
   tot.Invoke();   // Oppure: tot();

  • Scrive in Console: A, B, C.

   // Si toglie un delegato e si invoca il delegato multicast.
   tot -= b;
   tot.Invoke();   // Oppure: tot();

  • Scrive in Console: A, C.

© 2022 Carlo Vecchio
Torna ai contenuti