Home > database >  Are there alternate ways to input a parameter into a task besides using lambda functions?
Are there alternate ways to input a parameter into a task besides using lambda functions?

Time:04-15

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.

  • Related