Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling Task<T> methods generically

Related to Casting TResult in Task<TResult> to System.Object. I am building a generic async command execution function for a service.

The method to execute a generic command looks like this:

public async Task<object> ExecuteCommandAsync(string cmdName, CommandData data)

The code uses reflection to find a method on the class that has the given cmdName. Invoking this will return a Type<T> - we don't know the T in advance. However since Task<T> is not covariant, I cannot cast to Task<object>.

At present my solution (as per 21805564) is to call the Result method and encapsulate in a Task.Run, as shown below:

// find the method
MethodInfo cmd = GetMethod(cmdName);
var task = (Task)cmd.Invoke(this, data);
return await Task.Run<object>(() => { return task.GetType().GetProperty("Result").GetValue(task); });

My concern is that doing this negates the value of async: getting the result is now a blocking call so I might as well have used synchronous methods anyway.

like image 553
Quango Avatar asked Dec 26 '22 01:12

Quango


1 Answers

Remember that dynamic is available. You can do something like this as long as you're not using Microsoft.Bcl.Async:

public async Task<dynamic> ExecuteCommandAsync(string cmdName, CommandData data)
{
  MethodInfo cmd = GetMethod(cmdName);
  dynamic task = cmd.Invoke(this, data);
  return await task;
}

In particular, I don't recommend using Task.Run because that would waste a thread, and I don't recommend using Task.Result because it will encapsulate any exceptions inside an AggregateException.

like image 55
Stephen Cleary Avatar answered Jan 11 '23 23:01

Stephen Cleary