2013-03-05 1 views
10

Ma question est détaillée dans le code suivant - la raison pour laquelle je demande c'est que j'expérimente avec les délégués:L'utilisation d'un délégué Multicast aux fonctions de la chaîne

//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 est retourné ....

enter image description here

toutes les fonctions feu, les différents messages « est appelé un » etc tous seront imprimés au console mais seulement le montant est revenu de la dernière fonction e est retourné - Je suppose dans le bac kground ils sont retournés mais ensuite écrasés?

+0

Afin d'obtenir ce genre de récursion, je pense que vous devriez passer dans le délégué précédent en tant que paramètre. Sinon, vous ajoutez seulement 10 + 6 = 16. –

+0

@DavinTryon Ce n'est pas vraiment récursif. Il n'y a aucune fonction s'appelant elle-même; c'est juste un enchaînement de fonctions. – Servy

+0

@Servy Ah oui, plus comme des fonctions "imbriquées"? Y a-t-il un nom de langage fonctionnel pour cela? –

Répondre

14

Lorsque vous avez un délégué multicast comme d dans votre question, la valeur de retour est la valeur de retour de la dernière méthode de la liste d'appel de d.

En général, pour les délégués de multidiffusion, il est très naturel d'utiliser le type de retour void.

Le compilateur n'avait aucune chance de deviner que vous espériez 10+2+3+4+5+6. Vous ne l'avez pas spécifié nulle part.

Vous pouvez changer votre type de délégué dans:

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

Ensuite, votre méthode a, par exemple, devrait ressembler à ceci:

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

Le délégué multicast exemple d serait alors appelé comme celui-ci :

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

J'espère que ce qu'il lps.

+1

+1 une approche relativement facile – whytheq

8

Ce n'est pas ainsi que les types de retour sont gérés pour les délégués. Ce qui se passera, c'est que tous les gestionnaires seront exécutés indépendamment l'un de l'autre, puis un sera choisi au hasard (techniquement c'est le gestionnaire qui a été souscrit en dernier, mais vous ne devriez pas compter dessus) pour être retourné à l'appelant qui a invoqué le délégué.

Je vous déconseillerais fortement d'utiliser un événement (vous traitez ce délégué comme s'il s'agissait d'un événement) qui a une valeur de retour. Le comportement n'est pratiquement jamais souhaitable. Si vous voulez une valeur de retour, il est logique de s'assurer que votre délégué est toujours mappé à exactement une fonction, ni plus, ni moins.

Comme pour générer effectivement le résultat souhaité, alors qu'il ya un certain nombre d'approches, vous seriez mieux servis avec une collection plus traditionnelle des délégués:

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

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

+1 très bon rapport qualité-prix –

+1

Les méthodes de la liste d'invocation sont appelées dans l'ordre, la valeur de retour du dernier de ces appels est utilisée (elle n'est pas choisie au hasard), et vous pouvez ** compter sur cela. Pour citer la spécification C#: _ "L'invocation d'une instance de délégué dont la liste d'invocation contient plusieurs entrées procède en invoquant chacune des méthodes de la liste d'invocation, de manière synchrone, dans l'ordre." _ Et plus la valeur de retour _ "proviendra de l'invocation Le même paragraphe garantit également que les paramètres 'ref' peuvent être utilisés, comme je le fais dans ma réponse. –

+0

@JeppeStigNielsen Oui, je le sais. Cela ne change pas le fait qu'il est très mauvais de s'en remettre, non parce que ce n'est pas défini et qu'il peut changer, mais parce que ce n'est pas bien connu, et non pas que les lecteurs du code s'attendent à être bien définis. Il devient également très difficile de raisonner dans un environnement multithread, ce qui est particulièrement fréquent lorsqu'il s'agit d'événements dans ce manoir. Ma réponse est claire à ce sujet; c'est en effet * possible *, c'est juste une très mauvaise idée. – Servy