2010-01-20 5 views
10

Je suis un développeur C++ ayant utilisé les signaux & slots en C++ qui me semble être analogue aux délégués en C#. Je me suis retrouvé à la recherche de la fonctionnalité fournie par "bind", et je sens qu'il me manque quelque chose.(Comment) est-il possible de lier/relier une méthode pour travailler avec un délégué d'une signature différente?

Je pense que quelque chose comme ce qui suit, qui est possible en C++ devrait être possible dans C# avec des délégués. Voici quelques psudo-code pour ce que je ferais en C++:

Slot<void> someCallback; 

int foo(int i) 
{ 
    std::cout << "Value: " << i << "\n"; 
    return i; 
} 

int main() 
{ 
    int i = 0; 
    Slot<int> someCallback = bind(fun_ptr(foo), i); 
    ++i; // added to show that late evaluation would be a non-trivial difference 
    int result = someCallback(); 
    assert(result == 0); 
    return 0; 
} 

Malheureusement, je ne l'ai pas pu trouver aucune référence à la liaison/reconsolidation en ce qui concerne les délégués C#. Est-ce que je manque quelque chose? Y a-t-il une façon radicalement différente de le faire en C#?

Répondre

13

En C# nous faisons quelque chose comme ceci:

class Program { 
    static Action Curry<T>(Action<T> action, T parameter) { 
     return() => action(parameter); 
    } 

    static void Foo(int i) { 
     Console.WriteLine("Value: {0}", i); 
    } 
    static void Main(string[] args) { 
     Action curried = Curry(Foo, 5); 
     curried(); 
    } 
} 

Il est clair que la méthode Foo correspond à votre méthode Foo, juste avec les appels appropriés à Console.WriteLine au lieu de std::cout. Ensuite, nous déclarons une méthode Curry qui accepte un Action<T> et renvoie un Action. En général, un Action<T> est un délégué qui accepte un seul paramètre de type T et renvoie void. En particulier, Foo est un Action<int> car il accepte un paramètre de type int et renvoie void. En ce qui concerne le type de retour de Curry, il est déclaré comme Action. Un Action est un délégué qui n'a aucun paramètre et renvoie void.

La définition de Curry est plutôt intéressante. Nous définissons une action en utilisant une expression lambda qui est une forme très spéciale d'un délégué anonyme. En effet

() => action(parameter) 

dit que le paramètre void est mis en correspondance action évaluée à parameter.

Enfin, Main nous déclarons une instance de Action nommé curried qui est le résultat de l'application Curry à Foo avec le paramètre 5. Cela joue le même rôle que bind(fun_ptr(foo), 5) dans votre exemple C++.

Enfin, nous appelons le nouveau délégué curried via la syntaxe curried(). C'est comme someCallback() dans votre exemple.

Le terme fantaisie pour cela est currying.

A titre d'exemple plus intéressant, tenez compte des éléments suivants:

class Program { 
    static Func<TArg, TResult> Curry<TArg, TResult>(
     Func<TArg, TArg, TResult> func, 
     TArg arg1 
    ) { 
     return arg => func(arg1, arg); 
    } 

    static int Add(int x, int y) { 
     return x + y; 
    } 

    static void Main(string[] args) { 
     Func<int, int> addFive = Curry<int, int>(Add, 5); 
     Console.WriteLine(addFive(7)); 
    } 
} 

Ici, nous proclamons une méthode Curry qui accepte un délégué (Func<TArg, TArg, TResult> qui accepte deux paramètres du même type TArg et renvoie une valeur d'un autre taper TResult et un paramètre de type TArg et renvoie un délégué qui accepte un seul paramètre de type TArg et renvoie une valeur de type TResult (Func<TArg, TResult>).

Puis, en tant que test, nous déclarons une méthode Add qui accepte deux paramètres de type int et renvoie un paramètre de type int (un Func<int, int, int>). Ensuite, dans Main, nous instancions un nouveau délégué nommé addFive qui agit comme une méthode qui ajoute cinq à son paramètre d'entrée. Ainsi,

Console.WriteLine(addFive(7)); 

imprime 12 sur la console.

+0

Merci pour votre réponse détaillée. J'ai marqué l'autre comme la réponse acceptée parce qu'elle est plus concise, bien qu'il manque certains détails importants que vous avez inclus. – Catskul

+0

Bien sûr. J'espère juste que la couleur supplémentaire aide. :-) – jason

+0

Réponse extrêmement utile, ceci. Introduit un concept génial, currying, d'une manière facile à comprendre. Va certainement ajouter cela à ma boîte à outils mentale. –

4

Effectuez les opérations suivantes

class Example { 
    static void foo(int i) { 
    Console.WriteLine(i); 
    } 
    public static void Main() { 
    Action someCallback =() => foo(5); 
    someCallback(); 
    } 
} 

Ou quelque chose d'encore plus près de la contre-partie C de

class Example { 
    static void foo(int i) { 
    Console.WriteLine(i); 
    } 
    static Action bind<T>(Action<T> action, T value) { 
    return() => action(value); 
    } 
    public static void Main() { 
    Action someCallback = bind(foo, 5); 
    someCallback(); 
    } 
} 

Explication. Ce qui se passe ici, c'est que je crée un nouveau délégué au moyen d'une expression lambda. Le lambda est l'expression commençant par () =>. Dans ce cas, il crée un délégué n'acceptant aucun argument et ne produisant aucune valeur. Il est compatible avec le type Action.

+0

Woah. Intéressant. Que vois-je ici? – Catskul

+2

Il crée un nouveau délégué en utilisant une expression lambda. Le délégué appelle "foo (5)". C'est comme créer une nouvelle méthode à la volée pour appeler foo (5) pour vous, puis l'assigner au délégué someCallback. –

+0

'Action ' est un délégué fourni par le framework. C'est une fonction qui prend un paramètre (de type T) et ne renvoie rien (void). Il attribue une fonction anonyme à ce délégué (qu'il a appelé 'someCallback'). Les parenthèses vides indiquent qu'il ne prend aucun argument, l'expression après '=>' est le corps de la fonction. – Sapph

Questions connexes