Reputation: 885
I want to pass in an method that returns void and accepts a param int. How do i do that?
Can someone show me the non-lambda and lambda versions?
private void LongTask(int s)
{
Thread.Sleep(s* 1000);
}
public void Run()
{
Task q = Task.Run(Action<int>(LongTask));
}
Upvotes: 1
Views: 103
Reputation: 70671
In your example, you have not shown what value to pass to the method. Which makes it a bit difficult to know for sure what it is you are even asking. But I think you are asking for an illustration of how to call your method LongTask()
, passing it the appropriate value, as the Task.Run()
operation, and to provide this illustration using both the lambda syntax and non-lambda syntax.
Note: very little of this is actually specific to Task.Run()
. It all applies any time you are dealing with passing a delegate to a method where the delegate needs to represent a method with a different signature from that used in the method you are calling. That Task.Run()
is involved here is almost incidental.
Anyway…
Actually, since lambda expressions (in this usage) compile to anonymous methods, there are really three options:
"Old-school" anonymous method:
public void Run()
{
int someValue = 17;
Task q = Task.Run(delegate { LongTask(someValue); });
}
The above causes the compiler to create an anonymous method (delegate() { LongTask(someValue); }
), and generate code to create a delegate instance to pass to the Task.Run()
method. The type of the delegate is inferred from the usage (i.e. based on the one method overload that matches the anonymous method declaration).
Lambda anonymous method:
public void Run()
{
int someValue = 17;
Task q = Task.Run(() => LongTask(someValue));
}
The above is just like the previous example, except using the nominally more concise lambda syntax to declare the anonymous method. I say "nominally" because in this particular case, it's not really that much shorter. But there are other scenarios where the lambda syntax is much more convenient; typically, when the anonymous method is returning a value, since with lambda syntax the return
statement is implicit.
Explicit:
class A
{
private readonly int _value;
private readonly Action<int> _action;
public A(int value, Action<int> action)
{
_value = value;
_action = action;
}
public void M() { _action(_value); }
}
public void Run()
{
int someValue = 17;
Task q = Task.Run((Action)(new A(someValue, LongTask).M));
}
The above relies on an explicitly written class to serve the same purpose as the compiler-generated class that the anonymous method options result in. Depending on the exact scenario, the compiler-generated class may be significantly different in implementation. But the behavior will be essentially the same: an instance of the class is created, in which the desired value is stored, and a method of the appropriate signature is included in the class, where the body of that method is the code you want executed.
(Aside: In this case, for conciseness, I've shown an implementation that actually takes a delegate instance instead of hard-coding the target object and method call. This is in fact a more reusable implementation than the compiler actually would generate, mainly because the compiler has to deal with a lot more complicated scenarios and this is an optimization that's only useful in a very narrow scenario, i.e. where there's exactly one method being called. The compiler would have also stored this
and would have copied the body of the anonymous method to the generated method, using the stored this
reference and any other captured variables as needed, e.g. to call the LongTask()
method.)
In the above the variable _value
is readonly
, but in anonymous methods they would generally be mutable variables captured from the context of the anonymous method. To better emulate how an anonymous method typically would work, you'd want something more like this:
class A
{
public int value;
private readonly Action<int> _action;
public A(int value, Action<int> action)
{
this.value = value;
_action = action;
}
public void M() { _action(value); }
}
public void Run()
{
A a = new A(17, LongTask);
Task q = Task.Run((Action)a.M);
a.value = 19;
}
Note that in the above, the value
is a public field. And the code actually modifies the variable after calling Task.Run()
. This is akin to changing the value of someValue
in the anonymous method examples (again, after calling Task.Run()
), and has the same risk: if the assignment after the Task.Run()
method is executed before the task can execute the method call, then you get a different value passed to the method than if the task can execute the method first.
Moral: always watch out for your captured variables, and make sure they have the lifetime and value you expect them to have.
By the way, the cast to the desired delegate type Action
is required in these last two examples, because otherwise the Task.Run()
overload to select is ambiguous to the compiler, between Task.Run(Action)
and Task.Run(Func<Task>)
. Short version: the return type of the delegate signature is ignored when trying to convert from a method group to a delegate type and match it to a particular overload. For the longer version, see Compiler Ambiguous invocation error - anonymous method and method group with Func<> or Action.
Upvotes: 1