1

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
}
JasonC
  • 139
  • 8

2 Answers2

3

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));
}
Woody1193
  • 7,252
  • 5
  • 40
  • 90
  • 1
    Can I go back to bed? It's only 10am but I'm sleepy :D Have a good one. – ProgrammingLlama Apr 15 '22 at 01:01
  • 1
    Makes sense that I want to send the method, not the result of the method to `Task.Run()` – JasonC Apr 15 '22 at 01:06
  • this is only a wrapper, right? if the OP doesn't want add a lambda just to pass parameters, why does he like add an extension method for that? – Lei Yang Apr 15 '22 at 02:36
  • @LeiYang If, for instance, he has many usages of this pattern and just wants some syntax sugar on top. But there are many potential solutions to this issue – Woody1193 Apr 15 '22 at 04:46
2

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.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104