The term for what you are trying to do is a closure -- when you have a single object that contains all of the information needed to call a method, including the call site and the parameters.
To make closures in C#, you need anonymous delegates, which these days means lambda expressions. Given a named method like MethodA
, you cannot pass an "invocation" of that method, with parameter, directly into another method like this:
public void Run(Action<int> method) { }
public void MethodA(int x ) { }
// Does *not* work
Run(MethodA(5));
Because MethodA(5)
is no longer a method, it's an expression whose type is void
, and that's wrong. However, you can wrap up this method call into a closure that is of type Action<int>
:
Run(x => MethodA(5));
This gives you exactly what you asked for: you passed in an Action<int>
to the Run
method and trapped the parameter at the call site. However, this is probably not what you actually want: if you pass an Action<int>
into the Run
method, that method is going to assume it needs to pass a parameter into the method call itself, but your lambda is going to ignore it. Instead, you can do as @SLaks suggests, and remove the parameter completely:
public void Run(Action method) { }
public void MethodA(int x ) { }
Run(() => MethodA(5));
In this case, there is no parameter going in to the lambda expression, so the resulting delegate will be of type Action
; the fact that you ultimately called a method that took a parameter is hidden inside the closure.
This is a fairly popular technique, so obviously there are some benefits:
- The do exactly what you ask: allow us to capture parameters or values that will be out of scope by the time the delegate is actually invoked. Note that you can use variables in your closure, e.g.
var x = 6; Run(() => MethodA(x))
, and the variablex
will remain available to your closure even after it goes out of scope. - They are more flexible than passing just a named method, because your lambda can be complex, e.g.:
Run(() => { MethodA(5); MethodB(5) });
- The 'functional' style of programming that is encourage by lambdas is very popular these days, as shown by the number of functional elements C# has gained over the past few versions.
However, there is a tradeoff, mostly in terms of how much control you have over what the lambda does. For example, in your case you have a Run
method that initially took an Action<int>
and an int
and called one with the other. That probably means that your Run
method assumes that, at some point, the method you pass into it will do something with some integer value (look it up in a database, compute some result with it, etc.).
If you instead switch over to a closure, you now allow the caller to do literally anything they want in the passed-in delegate, e.g.:
Run(() => Console.WriteLine("hahaha! No int here!"));
That may be perfectly fine in your case; you may not really care what goes on inside the delegate. But if your logic assumes something particular about the method that is being passed in, using anonymous delegates can very easily circumvent those expectations.