(How) is it possible to bind/rebind a method to work with a delegate of a different signature?
I'm a c++ developer having used signals & slots in c++ which to me seems to be analogous to delegat开发者_JAVA百科es in c#. I've found myself at a loss in searching for the functionality provided by "bind", and feel I must be missing something.
I feel like that something like the following, which is possible in c++ should be possible in c# with delegates. Here is some psudo-code for what I would do in 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;
}
Unfortunately, I've not been able to find any reference to binding/rebinding with regards to c# delegates. Am I missing something? Is there some radically different way to do this in c#?
In C# we do something like this:
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();
}
}
Clearly the method Foo
corresponds to your method Foo
, just with the appropriate calls to Console.WriteLine
instead of std::cout
.
Next, we declare a method Curry
that accepts an Action<T>
and returns an Action
. In general, an Action<T>
is a delegate that accepts a single parameter of type T
and returns void
. In particular, Foo
is an Action<int>
because it accepts one parameter of type int
and returns void
. As for the return type of Curry
, it is declared as an Action
. An Action
is a delegate the has no parameters and returns void
.
The definition of Curry
is rather interesting. We are defining an action using a lambda expression which is a very special form of an anonymous delegate. Effectively
() => action(parameter)
says that the void
parameter is mapped to action
evaluated at parameter
.
Finally, in Main
we are declaring an instance of Action
named curried
that is the result of applying Curry
to Foo
with the parameter 5
. This plays the same role as bind(fun_ptr(foo), 5)
in your C++ example.
Lastly, we invoke the newly formed delegate curried
via the syntax curried()
. This is like someCallback()
in your example.
The fancy term for this is currying.
As a more interesting example, consider the following:
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));
}
}
Here we are declaring a method Curry
that accepts a delegate (Func<TArg, TArg, TResult>
that accepts two parameters of the same type TArg
and returns a value of some other type TResult
and a parameter of type TArg
and returns a delegate that accepts a single parameter of type TArg
and returns a value of type TResult
(Func<TArg, TResult>
).
Then, as a test we declare a method Add
that accepts two parameters of type int
and returns a parameter of type int
(a Func<int, int, int>
). Then in Main
we instantiate a new delegate named addFive
that acts like a method that adds five to its input parameter. Thus
Console.WriteLine(addFive(7));
prints 12
on the console.
Try the following
class Example {
static void foo(int i) {
Console.WriteLine(i);
}
public static void Main() {
Action someCallback = () => foo(5);
someCallback();
}
}
Or for something even closer to the C++ counter part
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();
}
}
Explanation. What's happening here is that I am creating a new delegate by means of a lambda expression. The lambda is the expression starting with () =>
. In this case it creates a delegate accepting no arguments and producing no value. It is compatible with the type Action
.
精彩评论