I'm a c++ developer having used signals & slots in c++ which to me seems to be analogous to delegates 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:
Clearly the method
Foo
corresponds to your methodFoo
, just with the appropriate calls toConsole.WriteLine
instead ofstd::cout
.Next, we declare a method
Curry
that accepts anAction<T>
and returns anAction
. In general, anAction<T>
is a delegate that accepts a single parameter of typeT
and returnsvoid
. In particular,Foo
is anAction<int>
because it accepts one parameter of typeint
and returnsvoid
. As for the return type ofCurry
, it is declared as anAction
. AnAction
is a delegate the has no parameters and returnsvoid
.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. Effectivelysays that the
void
parameter is mapped toaction
evaluated atparameter
.Finally, in
Main
we are declaring an instance ofAction
namedcurried
that is the result of applyingCurry
toFoo
with the parameter5
. This plays the same role asbind(fun_ptr(foo), 5)
in your C++ example.Lastly, we invoke the newly formed delegate
curried
via the syntaxcurried()
. This is likesomeCallback()
in your example.The fancy term for this is currying.
As a more interesting example, consider the following:
Here we are declaring a method
Curry
that accepts a delegate (Func<TArg, TArg, TResult>
that accepts two parameters of the same typeTArg
and returns a value of some other typeTResult
and a parameter of typeTArg
and returns a delegate that accepts a single parameter of typeTArg
and returns a value of typeTResult
(Func<TArg, TResult>
).Then, as a test we declare a method
Add
that accepts two parameters of typeint
and returns a parameter of typeint
(aFunc<int, int, int>
). Then inMain
we instantiate a new delegate namedaddFive
that acts like a method that adds five to its input parameter. Thusprints
12
on the console.Try the following
Or for something even closer to the C++ counter part
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 typeAction
.