I have static class full of extension methods where each of the methods is asynchronous and returns some value - like this:
public static class MyContextExtensions{ public static async Task<bool> SomeFunction(this DbContext myContext){ bool output = false; //...doing stuff with myContext return output; } public static async Task<List<string>> SomeOtherFunction(this DbContext myContext){ List<string> output = new List<string>(); //...doing stuff with myContext return output; } }
My goal is to be able to invoke any of these methods from a single method in another class and return their result as an object. It would look something like this:
public class MyHub: Hub{ public async Task<object> InvokeContextExtension(string methodName){ using(var context = new DbContext()){ //This fails because of invalid cast return await (Task<object>)typeof(MyContextExtensions).GetMethod(methodName).Invoke(null, context); } } }
The problem is that the cast fails. My dilemma is that I cannot pass any type parameters to the "InvokeContextExtension" method because it is part of a SignalR hub and is invoked by javascript. And to a certain extent I don't care about the return type of the extension method because it is just going to get serialized to JSON and sent back to the javascript client. However I do have to cast the value returned by Invoke as a Task in order to use the await operator. And I have to supply a generic parameter with that "Task" otherwise it will treat the return type as void. So it all comes down to how do I successfully cast Task with generic parameter T to a Task with a generic parameter of object where T represents the output of the extension method.
You should return Task<T> if you would like to write an asynchronous method that returns a value. If you would like to write an event handler, you can return void instead. Until C# 7.0 an asynchronous method could return Task, Task<T>, or void.
Using this Task<T> class we can return data or values from a task. In Task<T>, T represents the data type that you want to return as a result of the task. With Task<T>, we have the representation of an asynchronous method that is going to return something in the future.
The task plugin event handler can return a value or a promise. The command will fail if undefined is returned or if the promise is resolved with undefined .
Return Task from asynchronous method When a method returns a task, we can use the await keyword to call it. Note: The await keyword should be within a function qualified by an async keyword (in other words asynchronous), otherwise the compiler will treat it as a normal synchronous function.
You can do it in two steps - await
the task using the base class, then harvest the result using reflection or dynamic
:
using(var context = new DbContext()) { // Get the task Task task = (Task)typeof(MyContextExtensions).GetMethod(methodName).Invoke(null, context); // Make sure it runs to completion await task.ConfigureAwait(false); // Harvest the result return (object)((dynamic)task).Result; }
Here is a complete running example that puts in context the above technique of calling Task
through reflection:
class MainClass { public static void Main(string[] args) { var t1 = Task.Run(async () => Console.WriteLine(await Bar("Foo1"))); var t2 = Task.Run(async () => Console.WriteLine(await Bar("Foo2"))); Task.WaitAll(t1, t2); } public static async Task<object> Bar(string name) { Task t = (Task)typeof(MainClass).GetMethod(name).Invoke(null, new object[] { "bar" }); await t.ConfigureAwait(false); return (object)((dynamic)t).Result; } public static Task<string> Foo1(string s) { return Task.FromResult("hello"); } public static Task<bool> Foo2(string s) { return Task.FromResult(true); } }
In general, to convert a Task<T>
to Task<object>
, I would simply go for the straightforward continuation mapping :
Task<T> yourTaskT; // .... Task<object> yourTaskObject = yourTaskT.ContinueWith(t => (object) t.Result);
(documentation link here)
However, your actual specific need is to invoke a Task
by reflection and obtain its (unknown type) result .
For this, you can refer to the complete dasblinkenlight's answer, which should fit your exact problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With