giovedì 27 luglio 2017

C# DELEGATE - PARTE 2 - MULTICASTDELEGATE

Benvenuti in un approfondimento, firmato Programmazione Applicata, dove andremo a capire cosa sono i delegati in ambiente Microsoft .Net e in quali modi andarli ad utilizzare. La fonte principale del contenuto teorico di questi articoli è la documentazione ufficiale Microsoft [link], tutti gli esempi che tratteremo saranno in linguaggio C#.


Riprendiamo da dove eravamo rimasti

Nel precedente articolo [link] abbiamo visto cosa sono i delegati e come si usano, oggi andiamo ad affrontare un aspetto più peculiare dei delegati: abbiamo visto come sia possibile assegnare un metodo ad un delegato per farlo eseguire ma i delegati sono puntatori a metodi capaci di indirizzarne più di uno alla volta. Ma andiamo per passi.

System.Delegate

Che cos'è un delegato? si lo so, lo abbiamo già visto ma sto chiedendo che cosa è un delegato a livello di framework? un delegato è un tipo di dato, come int, string o bool, quindi corrisponde ad un oggetto del framework Microsoft .Net che lo incapsula: System.Delegate [link]

Dunque quando andiamo a scrivere un delegato stiamo preparando un oggetto di tipo System.Delegate a cui attribuire come valore il riferimento ad un metodo (con firma compatibile). Un programmatore, una volta istanziato l'oggetto, può manipolarne il contenuto andando a leggere/scrivere la variabile ed effettuare operazioni di somma e sottrazione, vediamo un esempio banale:

Int32 a, b, c;
a=2;
b=3;
c=a+b; //c=5

Semplicemente questo esempio non fa altro che sommare due numeri interi ed assegnarli ad un terzo valore. Ma che succede quando invece che utilizzare il tipo di dato Int32 utilizzo un System.Delegate?

public delegate void TestDelegate();
...
public void Hello(){ 
   Console.WriteLine("hello world");
}
...
public void Hi(){
   Console.WriteLine("Hi");
}
...
TestDelegate a,b,c;
a=Hello;
b=Hi;
c=a+b; //c=???

Il fatto è che il codice appena scritto funziona, vediamo perchè.

System.MulticastDelegate

Quando ad un istanza di tipo System.Delegate vengono assegnati due valori questo diventa un oggetto di tipo System.MulticastDelegate (erede di System.Delegate), la cui unica differenza è la capacità di puntare a più di un metodo.

Riprendendo l'esempio precedente, otteniamo il seguente risultato:

TestDelegate a,b,c;
a=Hello();
b=Hi();
c=a+b;
c();
//output:
//hello world
//Hi
c-=b;
c();
//output:
//hello world

Eseguendo il delegato C, la prima volta vengono eseguiti i delegati A e B, la seconda solo il delegato A.

La verità è che fin da subito il Framework Microsoft .Net istanzia un oggetto di tipo System.MulticastDelegate perchè, ereditando dalla classe System.Delegate, è già pronto ad operare sia come delegato singolo che multiplo. 

Non è programmazione concorrente

Prestiamo attenzione a come si comporta un MulticastDelegate quando viene eseguito, nell'esempio precedente abbiamo visto come i metodi che sono stati assegnati all'istanza vengono eseguiti nell'ordine preciso con cui vengono scritti. Questo significa che, se per qualche ragione uno dei metodi contiene un rallentamento nell'esecuzione (come uno sleep) gli altri subiranno un ritardo proporzionato.

Vediamo meglio questo esempio:
public delegate void DoSomething();

public void DoItAndWait()
{
    Console.Write("hello...");
    System.Threading.Thread.Sleep(5000);
    Console.WriteLine("world!");
}

public void DoItSilently()
{
    Console.WriteLine("shh! hello world");
}

public void Run()
{
    DoSomething delegates;
    delegates = DoItAndWait;
    delegates += DoItSilently;
    delegates();
    //output:
    //hello..(wait 5 seconds) world!
    //shh! hello world
}

public static void Main(String[] args)
{
    new Program().Run();
}

In questo esempio, dopo aver chiamato il primo metodo (DoItAndWait) l'intero programma viene arrestato e non c'è modo di andare avanti finchè l'esecuzione del metodo non viene terminata.

Esecuzione Asincrona

Per ovviare al problema che abbiamo appena descritto, i delegati possono essere eseguiti in modo asincrono, permettendo una maggiore gestione della esecuzione concorrente:

public delegate void DoSomething();

public void DoItAndWait()
{
    Console.Write("hello...");
    System.Threading.Thread.Sleep(5000);
    Console.WriteLine("world!");
}

public void DoItSilently()
{
    Console.WriteLine("shh! hello world");
}


public void Run()
{
    DoSomething waiting, silently;
    waiting = DoItAndWait;
    silently = DoItSilently;
    waiting.BeginInvoke(null,null);
    silently.BeginInvoke(null,null);
    //output:
    //hello...shh! hello world
    //(wait 5 seconds)
    //world!
}

public static void Main(String[] args)
{
    new Program().Run();
    Console.Read();
}

In questo esempio non abbiamo utilizzato un oggetto di tipo System.MulticastDelegate perchè l'esecuzione del metodo BeginInvoke è permessa solo quando applicata ad un solo metodo. Esiste un workaround per poter eseguire in modo asincrono i delegati di un System.MulticastDelegate:
public delegate void DoSomething();

public void DoItAndWait()
{
    Console.Write("hello...");
    System.Threading.Thread.Sleep(5000);
    Console.WriteLine("world!");
}

public void DoItSilently()
{
    Console.WriteLine("shh! hello world");
}


public void Run()
{
    DoSomething multicastHello;
    multicastHello = DoItAndWait;
    multicastHello += DoItSilently;
    foreach(DoSomething d in multicastHello.GetInvocationList()) 
       d.BeginInvoke(null,null);
}

public static void Main(String[] args)
{
    new Program().Run();
    Console.Read();
}



Nessun commento:

Posta un commento