2013-03-05 5 views
10

La mia domanda è dettagliato nel seguente codice - il motivo che sto chiedendo questo è che sto sperimentando con i delegati:Utilizzo di un delegato multicast alle funzioni catena

//create the delegate   
delegate int del(int x); 

class Program { 


    static void Main(string[] args) { 

     Program p; 
     p = new Program(); 

     del d = p.a; 
     d += p.b; 
     d += p.c; 
     d += p.d; 
     d += p.e; 
     Console.WriteLine(d(10)); //<<was hoping it would be 10+2+3+4+5+6 

     Console.WriteLine("press [enter] to exit"); 
     Console.ReadLine(); 
    } 

    private int a(int x) { Console.WriteLine("a is called"); return x + 2; } 
    private int b(int x) { Console.WriteLine("b is called"); return x + 3; } 
    private int c(int x) { Console.WriteLine("c is called"); return x + 4; } 
    private int d(int x) { Console.WriteLine("d is called"); return x + 5; } 
    private int e(int x) { Console.WriteLine("e is called"); return x + 6; } 

} 

16 viene restituito ....

enter image description here

tutte le funzioni fuoco, come i vari messaggi "una si chiama" ecc tutti vengono stampati al console ma solo l'importo restituito dall'ultima funzione di e viene restituito - sto assumendo nel bac kground vengono restituiti ma poi sovrascritti?

+0

Per ottenere questo tipo di ricorsione, penso che si debba passare al delegato precedente come parametro. Altrimenti, stai aggiungendo solo 10 + 6 = 16. –

+0

@DavinTryon Questa non è una vera ricorsione. Non c'è alcuna funzione che si chiama da sola; è solo concatenazione di funzioni. – Servy

+0

@Servy Ah sì, più come funzioni "annidate"? Esiste un nome di linguaggio funzionale per questo? –

risposta

14

Quando si dispone di un delegato multicast come d nella vostra domanda, il valore restituito è il valore restituito dallo ultimo metodo dell'elenco di chiamate di d.

In generale, per i delegati multicast, è più naturale utilizzare il tipo di ritorno void.

Il compilatore non ha avuto la possibilità di indovinare che speravi di 10+2+3+4+5+6. Non l'hai specificato da nessuna parte.

Si potrebbe modificare il tipo di delegato in:

delegate void del(int xToAdd, ref int sum); 

Poi il metodo a, ad esempio, dovrebbe essere simile a questo:

private void a(int x, ref int sum) { Console.WriteLine("a is called"); sum += x + 2; } 

Il delegato multicast esempio d sarebbe poi chiamato come questo :

int sum = 0; 
d(10, ref sum); 
Console.WriteLine(sum); 

Spero che sia lui LP.

+1

+1 un approccio relativamente facile – whytheq

8

Non è così che vengono gestiti i tipi di ritorno per i delegati. Quello che succederà è che tutti i gestori saranno eseguiti indipendentemente l'uno dall'altro, e quindi uno sarà scelto a caso (tecnicamente è il gestore che è stato sottoscritto per ultimo, ma non dovresti fare affidamento su quello) per essere restituito al chiamante che ha invocato il delegato.

Vorrei fortemente scoraggiarvi dal mai utilizzare un evento (si sta trattando questo delegato come se fosse un evento) che ha un valore di ritorno. Il comportamento non è praticamente mai desiderabile. Se si desidera un valore restituito, è opportuno assicurarsi che il delegato sia sempre mappato esattamente a una funzione, né più né meno.

Per quanto riguarda in realtà la generazione il risultato desiderato, mentre ci sono un certo numero di approcci, si sarebbe meglio servita con una collezione più tradizionale dei delegati:

List<Func<int, int>> functions = new List<Func<int, int>>(); 
//populate 

int result = functions.Aggregate(10, (total, func) => func(total)); 
+0

+1 buona notizia sul valore di ritorno –

+1

I metodi nella lista di invocazione sono chiamati in ordine, il valore di ritorno dell'ultima di quelle chiamate è usato (non è scelto a caso), un ** puoi ** contare su quello. Per citare la specifica C#: _ "Il richiamo di un'istanza delegata il cui elenco di invocazione contiene più voci procede richiamando ciascuno dei metodi nell'elenco di chiamata, in modo sincrono, nell'ordine." _ E inoltre il valore di ritorno _ "verrà dall'invocazione dell'ultimo delegato nella lista. "_ Lo stesso paragrafo garantisce anche che i parametri' ref' possano essere usati, come il modo in cui lo faccio nella mia risposta. –

+0

@JeppeStigNielsen Sì, lo so. Ciò non cambia il fatto che è una pessima pratica affidarsi a questo, non perché sia ​​indefinito e possa cambiare, ma perché non è ben noto, e non è un comportamento che i lettori del codice dovrebbero * prevedere * per essere ben definito. Diventa anche molto difficile ragionare in un ambiente multithread, che è particolarmente comune quando si tratta di eventi in questo maniero. La mia risposta è chiara su questo; è davvero * possibile *, è solo una pessima idea. – Servy