Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cast Task<T> to Task<object> in C# without having T

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.

like image 629
ncarriker Avatar asked Dec 30 '17 12:12

ncarriker


People also ask

How do I return a Valuetask?

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.

How to Get Value from Task in c#?

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.

Can Task return a Value?

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 .

How to return Task from async method?

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.


2 Answers

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);     } } 
like image 86
Sergey Kalinichenko Avatar answered Sep 21 '22 13:09

Sergey Kalinichenko


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.

like image 21
Pac0 Avatar answered Sep 19 '22 13:09

Pac0