2010-01-20 14 views
10

Soy un desarrollador de C++ con las señales usadas & en C++ que para mí parece ser análogo a los delegados en C#. Me he encontrado perdido en la búsqueda de la funcionalidad proporcionada por "bind", y siento que me falta algo.(¿Cómo) es posible vincular/volver a enlazar un método para trabajar con un delegado de una firma diferente?

Creo que algo como lo siguiente, que es posible en C++, debería ser posible en C# con los delegados. Aquí hay alguna psudo-código para lo que haría 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; 
} 

Por desgracia, no hemos sido capaces de encontrar ninguna referencia a la unión/reencuadernación lo que respecta a los delegados de C#. ¿Me estoy perdiendo de algo? ¿Hay alguna forma radicalmente diferente de hacer esto en C#?

Respuesta

13

En C# que hacer algo como esto:

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(); 
    } 
} 

Es evidente que el método Foo corresponde a su método de Foo, sólo con las llamadas apropiadas a Console.WriteLine en lugar de std::cout.

A continuación, declaramos un método Curry que acepta un Action<T> y devuelve un Action. En general, un Action<T> es un delegado que acepta un único parámetro del tipo T y devuelve void. En particular, Foo es un Action<int> porque acepta un parámetro del tipo int y devuelve void. En cuanto al tipo de devolución de Curry, se declara como Action. Un Action es un delegado que no tiene parámetros y devuelve void.

La definición de Curry es bastante interesante. Estamos definiendo una acción usando una expresión lambda que es una forma muy especial de un delegado anónimo. Efectivamente

() => action(parameter) 

dice que el parámetro void se asigna a action evaluada en parameter.

Por último, en Main estamos declarando una instancia de Action llamado curried que es el resultado de aplicar a CurryFoo con el parámetro 5. Esto juega el mismo rol que bind(fun_ptr(foo), 5) en su ejemplo de C++.

Por último, invocamos al delegado recién formado curried mediante la sintaxis curried(). Esto es como someCallback() en su ejemplo.

El término sofisticado para esto es currying.

Como un ejemplo más interesante, tenga en cuenta lo siguiente:

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)); 
    } 
} 

Aquí estamos declarando un método Curry que acepta un delegado (Func<TArg, TArg, TResult> que acepta dos parámetros del mismo tipo TArg y devuelve un valor de algún otro escriba TResult y un parámetro de tipo TArg y devuelve un delegado que acepta un único parámetro de tipo TArg y devuelve un valor de tipo TResult (Func<TArg, TResult>).

Luego, como prueba declaramos un método Add que acepta dos parámetros del tipo int y devuelve un parámetro del tipo int (a Func<int, int, int>). Luego, en Main instanciamos un nuevo delegado llamado addFive que actúa como un método que agrega cinco a su parámetro de entrada. Por lo tanto,

Console.WriteLine(addFive(7)); 

imprime 12 en la consola.

+0

Gracias por su respuesta detallada. Marqué el otro como la respuesta aceptada porque es más conciso, aunque le faltan algunos detalles importantes que el suyo incluyó. – Catskul

+0

Claro. Solo espero que el color extra te ayude. :-) – jason

+0

Respuesta extremadamente útil, esto. Introdujo un concepto brillante, currying, de una manera fácil de entender. Definitivamente agregaré esto a mi caja de herramientas mental. –

4

intente lo siguiente

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

O algo aún más cerca de la parte C++ contador

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(); 
    } 
} 

Explicación. Lo que sucede aquí es que estoy creando un nuevo delegado por medio de una expresión lambda. La lambda es la expresión que comienza con () =>. En este caso, crea un delegado que no acepta argumentos y no produce ningún valor. Es compatible con el tipo Action.

+0

Woah. Interesante. ¿Qué estoy viendo aquí? – Catskul

+2

Está creando un nuevo delegado usando una expresión lambda. El delegado llama "foo (5)". Es como hacer un nuevo método sobre la marcha para llamar a foo (5) por usted, y luego asignarlo al delegado de SomeCallback. –

+0

'Acción ' es un delegado proporcionado por el marco. Es una función que toma un parámetro (de tipo T) y no devuelve nada (vacío). Está asignando una función anónima a ese delegado (que ha llamado 'someCallback'). Los parens vacíos indican que no requiere argumentos, la expresión después de '=>' es el cuerpo de la función. – Sapph

Cuestiones relacionadas