Anonymous methods, servono?

Me lo sono chiesto più volte, ne avevo intuito il potenziale ma non trovavo casi concreti di utilizzo, prima di oggi... Credo di aver colto un aspetto fondamentale del metodo anonimo, cioè la differenza tra il contesto di generazione del metodo e quello di esecuzione...  Meglio fare un esempio va 

Supponiamo di avere queste due classi:

class Publisher
{
    
public event EventHandler<MyEventArgs> TestEvent;
    
    
public void Test()
    {
        
if (TestEvent != null)
            TestEvent(
thisnew MyEventArgs());
    }
}
    
class Subscriber
{
    
public void Call(object sender, MyEventArgs e)
    {
        
//do something
    
}
}

e supponiamo di voler sottoscrivere l'evento TestEvent di Publisher col metodo Call di Subscriber, ma di volerlo fare alla "loosely coupled event", ovvero senza sottoscriverlo direttamente ma con "qualcuno" in mezzo che disaccoppi totalmente i due. Supponiamo che questo qualcuno consenta infine di "pubblicare" eventi e di registrare "sottoscrittori". Potrebbe assomigliare a questa classe:

class Attacher
{    
    
public void Publish<TEventArgs>(object sender, string eventName, string publishName) where TEventArgs : EventArgs
    {
        Type t = sender.GetType();
        EventInfo ei = t.GetEvent(eventName);
        
if (ei != null)
        {
            ei.AddEventHandler(sender, 
                Delegate.CreateDelegate(
                    
typeof(EventHandler<TEventArgs>),
                    
this,
                    "Handler",
                    
false));
        }
    }

    
public void Subscribe(object target, string methodName, string publishName)
    {
        
//add subscribers to 'attachments' structure, skipped code...
    
}
}

 

attraverso publishName definisco un nome pubblico per un evento, ed attraverso questo stesso nome sottoscrivo gli eventi. Ora, tra le tante soluzioni possibili la prima che ho pensato era quella di definire una funzione in Attacher che avesse la firma corretta per poter sottoscrivere l'evento, e che si occupasse di redirezionare gli eventi sui sottoscrittori. Ho quindi aggiunto a Attacher questo prototipo di metodo:

public void Handler(object sender, MyEventArgs e)
{
    
if (attachments.ContainsKey("eventname"))
    {
        IList<Attachment> list = attachments["eventname"];
        
foreach (Attachment a in list)
            a.Invoke(
new object[] { sender, e });
    }
}

ma ecco qui 2 problemi: il primo sul tipo "cablato" MyEventArgs dell'argomento dell'evento, ma non è l'oggetto del post e quindi vado avanti , il secondo è come recuperare da una non meglio precisata struttura dati (attachments) i sottoscrittori dato il nome dell'evento, rappresentato dalla stringa "eventname" che evidentemente deve corrispondere al publishName passato al metodo Publish. Ma Handler è un altro metodo, e non sa niente di publishName...

Ecco qui che arriva il "metodo anonimo": se sostituisco la chiamata AddEventHandler in Publish vista sopra con la seguente:

ei.AddEventHandler(sender,
    
(EventHandler<TEventArgs>)delegate(object s, TEventArgs ea)
    {
       
if 
(attachments.ContainsKey( publishName ))
       {
            IList<Attachment> list = attachments[ publishName ];
                
foreach(Attachment a in 
list)
                    a.Invoke(
new object
[] {s, ea});
        }
    });

 

che riprende il codice del metodo Handler ma esplicita il parametro publishName, ecco che il nostro metodo viene "generato al volo", e la chiave con cui cerco i subscribers durante l'esecuzione dell'handler ora è proprio la publishName passata come parametro al metodo Publish, ma il suo valore NON dipende dal momento in cui il codice dell'handler verrà chiamato (in quel momento publishName non esiste), ma dal momento in cui il codice verrà generato, cioè quando chiamo Publish.

Insomma, non so se mi sono spiegato, ma è geniale, io già mi vedevo impelagato dietro ad astruse strutture dati ed invece con 3 linee di codice ho una soluzione pulitissima e (credo) efficiente. Insomma, gli "anonymous methods" a qualcosa servono... 

 

powered by IMHO 1.3