Are there alternate ways to input a parameter(s) into a task besides using lambda functions?
It seems like Task.Run(DoSomethingElse(myInput));
should work since Task.Run(DoSomething);
works but clearly it does not.
Using a lambda function only to convert a function with parameters to one without parameters seems odd, like an awkward cast. Task.Run(() => DoSomethingElse(myInput));
Main()
{
object myInput = new();
Task.Run(DoSomething); // Happy Code
Task.Run(() => DoSomethingElse(myInput)); // Happy Code
Task.Run(DoSomethingElse(myInput)); // CS1503 Argument 1: cannot convert from 'System.Threading.Tasks.Task' to 'SystemAction'
}
Task DoSomething()
{
// Something
}
Task DoSomethingElse(object input)
{
// Something else
}
CodePudding user response:
Looking at the documentation for Task.Run
, you'll notice that every definition takes either a Func<Task>
or a Func<Result>
or some combination therein. However, none of these definitions include a mention of parameters. Plus, what you're sending when you call Task.Run(DoSomethingElse(myInput))
is the result of calling DoSomethingElse(myInput)
because this represents a call to the method itself rather than sending the method and its arguments as a parameter. In effect, that's what using a Lambda does. If you really don't want to insert a lambda into your code you could try adding a static method like this:
public static Task Run<TItem>(f Func<TItem, Task>, i TItem) {
return Task.Run(() => f(i));
}
CodePudding user response:
The Task.Run
method is basically a shortcut for the Task.Factory.StartNew
method, with some default parameters. This is explained in this article by Stephen Toub: Task.Run vs Task.Factory.StartNew.
The Task.Factory.StartNew
has also an overload with an object state
parameter. You could use this overload for your own set of Task.Run
-like shortcuts.
You would need four of those, to cover all cases with-or-without-result and with-or-without-async-delegate:
static Task Run(Action<object> action, object state)
{
return Task.Factory.StartNew(action, state, default,
TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}
static Task Run(Func<object, Task> action, object state)
{
return Task.Factory.StartNew(action, state, default,
TaskCreationOptions.DenyChildAttach, TaskScheduler.Default).Unwrap();
}
static Task<TResult> Run<TResult>(Func<object, TResult> action, object state)
{
return Task.Factory.StartNew(action, state, default,
TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
}
static Task<TResult> Run<TResult>(Func<object, Task<TResult>> action, object state)
{
return Task.Factory.StartNew(action, state, default,
TaskCreationOptions.DenyChildAttach, TaskScheduler.Default).Unwrap();
}
Usage example:
Task task = Run(DoSomethingElse, myInput);
The advantage of this approach is that the myInput
is not captured in a closure, as it does when you use a lambda, so your program allocates less memory in the heap. It's an optimization with minuscule, and in most cases negligible, impact.