I DELEGATE

C sharpI delegate in C Sharp sono un nuovo tipo dopo le classi le struct e le interface. È un tipo reference le cui istanze ci permettono di trattare i metodi come se fossero oggetti. Un delegate è privo di implementazione, può essere definito a livello di namespace ma anche di classe. Vediamo come si dichiara un delegate.

namespace Delegate

{

    public delegate int MyDelegate(int a, int b);

}

Come vedi un delegate si dichiara con la parola chiave delegate, per il resto somiglia alla definizione di un metodo, abbiamo dichiarato un tipo di ritorno int e due parametri di tipo intero a e b. È un po’ come un metodo dichiarato all’interno di una interface, la particolarità è che nella dichiarazione c’è la keyword delegate. Questo particolare delegate rappresenta qualsiasi metodo che prenda in input due interi e restituisca un int.

 

UTILIZZO DEI DELEGATE

using System;
namespace Delegate
{
    public delegate int MyDelegate(int a, int b);
    class Program
    {
        static void Main(string[] args)
        {
            MyDelegate md = Somma;//PRIMA FORMA DI INIZIALIZZAZIONE DEL DELEGATE
            //MyDelegate md1 = new MyDelegate(Somma);SECONDA FORMA PER INIZIALIZZARE UN DELEGATE. MENO COMUNE.
            int result = md(10,5);
            Console.WriteLine($"Somma: {result}");
            md=Differenza;
            result = md(10,5);
            Console.WriteLine($"Differenza: {result}");
            md=Moltiplicazione;
            result = md.Invoke(10,5);//SI PUO' USARE ANCHE IL METODO Invoke PER CHIAMARE IL METODO.
            Console.WriteLine($"Moltiplicazione: {result}");
            
            static int Somma(int arg1, int arg2)
            {
                return arg1+arg2;
            }
            static int Differenza(int arg1, int arg2)
            {
                return arg1 - arg2;
            }
            static int Moltiplicazione(int arg1, int arg2)
            {
                return arg1 * arg2;
            }
        }
    }
}

Come vedi dal codice sopra riportato ho dichiarato tre metodi Somma, Differenza e Moltiplicazione ognuno avente la stessa signature del delegate. Con l’istruzione

MyDelegate md = Somma;

Ho inizializzato la variabile istanza del delegate con il metodo Somma, l’invocazione di tale metodo avviene con l’istruzione:

int result = md(10,5); o con l’istruzione

int result = md.Invoke(10,5);

Una volta effettuata la somma la stessa istanza del delegate può puntare un nuovo metodo. Ho utilizzato la parola puntare perché chi ha familiarità con il linguaggio C o C++ può osservare che un delegate è simile ad un puntatore a funzione, il problema dei puntatori è che spesso sono causa di bug difficili da scovare. Il C# invece ci mette a disposizione un meccanismo più trasparente e sicuramente meno incline a bug.

I DELEGATE IN C SHARP I DELEGATE MULTICAST

Vediamo di chiarire alcuni aspetti prima di introdurre il codice per un multicast delegate. Tale tipo di delegate o meglio la variabile istanza del delegate viene inizializzata come già visto. Con l’operatore += si assegnano all’istanza creata a più metodi, al momento dell’invocazione i metodi verranno eseguiti in cascata. Vediamo due implicazioni da conoscere:

  • Se il delegate restituisce un valore allora al momento dell’invocazione del multicast delegate verrà ritornato solo il valore dell’ultimo metodo eseguito. Quindi i delegate multicast vanno usati salvo rare eccezioni con il tipo restituito void.
  • Se viene generata un’eccezione in uno dei metodi la catena di invocazione si interrompe.
using System;
namespace Delegate
{
    public delegate void MyDelegate(int a, int b);
    class Program
    {
        static void Main(string[] args)
        {
            MyDelegate md = Somma;
            md += Differenza;
            md+=Moltiplicazione;
            md.Invoke(10,5);//SI PUO' USARE ANCHE IL METODO Invoke PER CHIAMARE IL METODO.
            
            static void Somma(int arg1, int arg2)
            {
                 Console.WriteLine($"Somma: {arg1 + arg2}");
            }
            static void Differenza(int arg1, int arg2)
            {
                Console.WriteLine($"Differenza: {arg1 - arg2}");
            }
            static void Moltiplicazione(int arg1, int arg2)
            {
               Console.WriteLine($"Moltiplicazione: {arg1 * arg2}");
            }
        }
    }
}

I DELEGATE IN C SHARP I DELEGATE GENERICI

using System;
namespace Delegate
{
    public delegate void MyDelegate<T>(T a, T b) where T:struct;
    class Program
    {
        static void Main(string[] args)
        {
            MyDelegate<int> md1 = Somma;
            MyDelegate<double> md2 = Differenza;
            MyDelegate<float> md3 = Moltiplicazione;
            md1.Invoke(12,7);
            md2.Invoke(100.45,90.78);
            md3.Invoke(12.8f, 13.4f);            
            static void Somma(int arg1, int arg2)
            {
                 Console.WriteLine($"Somma: {arg1 + arg2}");
            }
            static void Differenza(double arg1, double arg2)
            {
                Console.WriteLine($"Differenza: {arg1 - arg2}");
            }
            static void Moltiplicazione(float arg1, float arg2)
            {
               Console.WriteLine($"Moltiplicazione: {arg1 * arg2}");
            }
        }
    }
}

DELEGATE PREDEFINITI ACTION E FUNC

Il .NET Framework ci mette a disposizione due delegate predefiniti Action e Func. La differenza tra i due sta nel fatto che Action non restituisce valori, quindi il valore restituito è void e può accettare fino a 16 parametri di tipo. Il delegate Func viceversa lo usiamo quando abbiamo bisogno di un valore di ritorno. Vediamo come si presentano questi due delegate predefiniti.

public delegate void Action();

public delegate void Action(T arg);

public delegate void Action<T1,T2>(T1 arg1, T2 arg2);

public delegate TResult Func();

public delegate TResult Func<T1, TResult>(T1 arg);

public delegate TResult Func<T1, T2, TResult>(T1 arg1, T2 arg2);

using System;
namespace Delegate
{
    class Program
    {
        static void Main(string[] args)
        {
            Action<int,double> myAction = Somma;
            myAction+=Differenza;
            myAction+=Moltiplicazione;
            myAction.Invoke(10,89.98);            
            static void Somma(int arg1, double arg2)
            {
                 Console.WriteLine($"Somma: {arg1 + arg2}");
            }
            static void Differenza(int arg1, double arg2)
            {
                Console.WriteLine($"Differenza: {arg1 - arg2}");
            }
            static void Moltiplicazione(int arg1, double arg2)
            {
               Console.WriteLine($"Moltiplicazione: {arg1 * arg2}");
            }
            Func<double,double> MyFunc = RadiceQuadrata;
            double result = MyFunc.Invoke(144);
            Console.WriteLine(result);
            static double RadiceQuadrata(double arg1)
            {
                return Math.Sqrt(arg1);
            }
        }
    }
}

APPROFONDIMENTO SUI DELEGATE

In C#, un delegato è un tipo che rappresenta riferimenti a metodi con una particolare firma e tipo di ritorno. È simile a un puntatore a funzione in C++, ma è sicuro dal punto di vista del tipo e dell’ambiente. I delegati sono utilizzati per passare metodi come parametri a un’altra funzione. Questa funzionalità è particolarmente utile per la programmazione orientata agli eventi e per implementare callback.

Ecco una descrizione più dettagliata e un esempio di utilizzo dei delegati in C#:

Caratteristiche principali dei delegati:

1. Firma del Metodo: Un delegato può fare riferimento solo a metodi che abbiano una firma corrispondente (ossia, stesso tipo di ritorno e stessi parametri).

2. Multicast: I delegati possono essere concatenati insieme; cioè, possono fare riferimento a più metodi contemporaneamente. Quando viene invocato un delegato multicast, tutti i metodi ad esso associati vengono chiamati in sequenza.

3. Tipizzazione Sicura: I delegati sono sicuri dal punto di vista del tipo, il che significa che il compilatore controlla i tipi dei metodi assegnati ai delegati, prevenendo errori di tipo.

Definizione e Utilizzo di un Delegato:

Definizione di un Delegato

// Definizione di un delegato
public delegate void MyDelegate(string message);

Dichiarazione di Metodi Compatibili con il Delegato

// Metodo compatibile con il delegato
public void Method1(string message)
{
      Console.WriteLine(“Method1: ” + message);
}

public void Method2(string message)
{
      Console.WriteLine(“Method2: ” + message);
}

Assegnazione di Metodi al Delegato

// Creazione di un’istanza del delegato e assegnazione dei metodi
MyDelegate del = new MyDelegate(Method1);
del += Method2; // Aggiunta di un altro metodo al delegato (multicast)

// Invocazione del delegato
del(“Hello, World!“);

// Output:
// Method1: Hello, World!
// Method2: Hello, World!

LINK AI PRECEDENTI POST

LINK AL CODICE SU GITHUB