views:

60

answers:

2

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#?

+4  A: 

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.

JaredPar
Woah. Interesting. What am I seeing here?
Catskul
He's creating a new delegate using a lambda expression. The delegate calls "foo(5)". It's like making a new method on the fly to call foo(5) for you, then assigning it to the someCallback delegate.
Reed Copsey
`Action<T>` is a delegate provided by the framework. It's a function that takes one parameter (of type T) and returns nothing (void). He's assigning an anonymous function to that delegate (which he has named `someCallback`). The empty parens indicate that it takes no arguments, the expression after the `=>` is the body of the function.
Sapph
Thanks. That's exactly what I was looking for. Considering you provided this example, I don't suppose there is a generic bind available that automatically determines the return type?
Catskul
@Catskul, no there is not in C#
JaredPar
After using this more extensively it appears that there is a very big caveat to the first example which makes it un-useable: delayed evaluation. If I do "Action someCallback = () => foo(i);" it takes the "i" at time of the callback rather than the time of assignment.The second example however does work, but has the unfortunate side effect of needing a specific "bind" to be declared for each type of rebinding to be done.
Catskul
+3  A: 

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.

Jason
Thanks for your detailed answer. I marked the other as the accepted answer because it is more concise, though it is missing some important details that yours included.
Catskul
Sure. I just hope the extra color helps. :-)
Jason