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 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.
I'd like to provide an implementation which is IMHO the best combination of the earlier answers:
precise argument handling
no dynamic dispatch
general purpose extension method
Here you go:
/// <summary>
/// Casts a <see cref="Task"/> to a <see cref="Task{TResult}"/>.
/// This method will throw an <see cref="InvalidCastException"/> if the specified task
/// returns a value which is not identity-convertible to <typeparamref name="T"/>.
/// </summary>
public static async Task<T> Cast<T>(this Task task)
{
if (task == null)
throw new ArgumentNullException(nameof(task));
if (!task.GetType().IsGenericType || task.GetType().GetGenericTypeDefinition() != typeof(Task<>))
throw new ArgumentException("An argument of type 'System.Threading.Tasks.Task`1' was expected");
await task.ConfigureAwait(false);
object result = task.GetType().GetProperty(nameof(Task<object>.Result)).GetValue(task);
return (T)result;
}
You cannot cast Task<T> to Task<object>, because Task<T> is not covariant (it's not contravariant, either). The simplest solution would be to use some more reflection:
var task = (Task) mi.Invoke (obj, null) ;
var result = task.GetType ().GetProperty ("Result").GetValue (task) ;
This is slow and inefficient, but usable if this code is not executed often. As an aside, what is the use of having an asynchronous MakeMyClass1 method if you are going to block waiting for its result?
and Another possibility is to write an extension method to this purpose:
public static Task<object> Convert<T>(this Task<T> task)
{
TaskCompletionSource<object> res = new TaskCompletionSource<object>();
return task.ContinueWith(t =>
{
if (t.IsCanceled)
{
res.TrySetCanceled();
}
else if (t.IsFaulted)
{
res.TrySetException(t.Exception);
}
else
{
res.TrySetResult(t.Result);
}
return res.Task;
}
, TaskContinuationOptions.ExecuteSynchronously).Unwrap();
}
It is none-blocking solution and will preserve original state/exception of the Task.
The most efficient approach would be custom awaiter:
struct TaskCast<TSource, TDestination>
where TSource : TDestination
{
readonly Task<TSource> task;
public TaskCast(Task<TSource> task)
{
this.task = task;
}
public Awaiter GetAwaiter() => new Awaiter(task);
public struct Awaiter
: System.Runtime.CompilerServices.INotifyCompletion
{
System.Runtime.CompilerServices.TaskAwaiter<TSource> awaiter;
public Awaiter(Task<TSource> task)
{
awaiter = task.GetAwaiter();
}
public bool IsCompleted => awaiter.IsCompleted;
public TDestination GetResult() => awaiter.GetResult();
public void OnCompleted(Action continuation) => awaiter.OnCompleted(continuation);
}
}
with the following usage:
Task<...> someTask = ...;
await TaskCast<..., object>(someTask);
The limitation of this approach is that the result is not a Task<object> but an awaitable object.
I made a little extension method based on dasblinkenlight's answer:
public static class TaskExtension
{
public async static Task<T> Cast<T>(this Task task)
{
if (!task.GetType().IsGenericType) throw new InvalidOperationException();
await task.ConfigureAwait(false);
// Harvest the result. Ugly but works
return (T)((dynamic)task).Result;
}
}
Usage:
Task<Foo> task = ...
Task<object> = task.Cast<object>();
This way you can change T in Task<T> to anything you want.
For the best approach, without using reflection and dynamic ugly syntax, and without passing generic types. I would use two extension methods for achieving this goal.
public static async Task<object> CastToObject<T>([NotNull] this Task<T> task)
{
return await task.ConfigureAwait(false);
}
public static async Task<TResult> Cast<TResult>([NotNull] this Task<object> task)
{
return (TResult) await task.ConfigureAwait(false);
}
Usage:
Task<T1> task ...
Task<T2> task2 = task.CastToObject().Cast<T2>();
This my second approach, but not recommended:
public static async Task<TResult> Cast<TSource, TResult>([NotNull] this Task<TSource> task, TResult dummy = default)
{
return (TResult)(object) await task.ConfigureAwait(false);
}
Usage:
Task<T1> task ...
Task<T2> task2 = task.Cast((T2) default);
// Or
Task<T2> task2 = task.Cast<T1, T2>();
This my third approach, but not recommended: (similar to second one)
public static async Task<TResult> Cast<TSource, TResult>([NotNull] this Task<TSource> task, Type<TResult> type = null)
{
return (TResult)(object) await task.ConfigureAwait(false);
}
// Dummy type class
public class Type<T>
{
}
public static class TypeExtension
{
public static Type<T> ToGeneric<T>(this T source)
{
return new Type<T>();
}
}
Usage:
Task<T1> task ...
Task<T2> task2 = task.Cast(typeof(T2).ToGeneric());
// Or
Task<T2> task2 = task.Cast<T1, T2>();
This is not a good idea to mix await with dynamic/reflection invoke since await is a compiler instruction that generates a lot of code around invoked method and there is no real sense to "emulate" compiler work with more reflections, continuations, wrappers and etc.
Since what you need is to manage your code at RUN TIME then forget the asyc await syntax sugar which works at compile time. Rewrite SomeFunction and SomeOtherFunction without them, and start operations in your own tasks created at run time. You will get the same behavior but with crystal clear code.
Related
I am working on implementing pub/sub pattern for cache use. The following class is suppose to be used to publish any type of data. And subscribers can attach a callback of Action T on the event. But I couldn't create a generic event at the class level without making the class generic and I don't want to do that. So I have tried to cast the values back and forth to string using JsonSerializer as follows.
public class CacheNotification
{
public Action<string> CachePublished;
public Task Publish<T>(T value)
{
var json = JsonSerializer.Serialize(value);
OnCachePublished(json);
return Task.CompletedTask;
}
public Task Subscribe<T>(Action<T> callback)
{
Action<string> newCallback = (string json) =>
callback(JsonSerializer.Deserialize<T>(json));
CachePublished += newCallback;
return Task.CompletedTask;
}
protected virtual void OnCachePublished(string value)
{
if(CachePublished != null){
CachePublished.Invoke(value);
}
}
}
So the problem is on the subscribe method, the casting I attempted only works for Action string. It fails on Action int or Action object
public Task Subscribe<T>(Action<T> callback)
{
Action<string> newCallback = (string json) =>
callback(JsonSerializer.Deserialize<T>(json));
CachePublished += newCallback;
return Task.CompletedTask;
}
Here is a test I used. This fails for second subscriber
Action<string> sub1Callback = msg =>
{
sub1Counter++;
};
Action<int> sub2Callback = num =>
{
sub2Counter++;
};
var sub1 = await cache.Subscribe(sub1Callback);
var sub2 = await cache.Subscribe(sub2Callback);
await cache.Publish("Value1");
Assert.Equal(1, sub1Counter);
Assert.Equal(1, sub2Counter);
your problem is here:
Action sub2Callback = num =>
after
await cache.Publish("Value1");
Your:
public Task Subscribe<T>(Action<T> callback)
{
Action<string> newCallback = (string json) => callback(JsonSerializer.Deserialize<T>(json));
CachePublished += newCallback;
return Task.CompletedTask;
}
tries to deserialize "Value1" to integer and it correctly fails. It cannot be done.
Try to send integer value instead of "Value1" and it should work.
Another way is to refactor your Subscribe method to be independent of given type, but this cannot be done according to your intentions I guess.
Consider the following interface:
public interface IProvider
{
Task<bool> Contains(string key);
}
This is implementation satisfies Visual Studio
public Task<bool> Contains(string key)
{
return Task.FromResult(false);
}
This implementation is convenient to write and would seem to achieve the same thing:
public async Task<bool> Contains(string key)
{
return false;
}
However, Visual Studio throws a hissy-fit and insists:
This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await TaskEx.Run(...)' to do CPU-bound work on a background thread.
I'd love to just ignore that warning and avoid using Task.FromResult(...).
Are there any negative consequences to using the latter option?
The reason for that "hissy fit" is that the compiler needs to do a lot of work to present a task that works in all the expected right ways here, which you can see by compiling and decompiling it
Task.FromResult is cleaner, but may still have overhead - IIRC there are some scenarios where a Task.FromResult might work efficiently here (returning the same object each time), but I wouldn't rely on it.
There are 2 pragmatic reliable approaches:
return a reused static Task<bool> result each time
use ValueTask<bool> - which seems ideal here if you are returning synchronously a lot of the time
i.e.
private readonly static Task<bool> s_False = Task.FromResult(false);
public Task<bool> Contains(string key, string scope)
{
return s_False ;
}
or
public ValueTask<bool> Contains(string key, string scope)
{
return new ValueTask<bool>(false);
}
Note: the second of these may not be possible in this case, since you didn't define the interface. But: if you ever are designing an interface that needs to allow async usage but which may actually be sync: consider using ValueTask<T> as the exchange type, not Task<T>.
The generated C# of:
public async System.Threading.Tasks.Task<bool> Contains(string key, string scope)
{
return false;
}
is something like:
[StructLayout(LayoutKind.Auto)]
[CompilerGenerated]
private struct <Contains>d__0 : IAsyncStateMachine
{
public int <>1__state;
public AsyncTaskMethodBuilder<bool> <>t__builder;
private void MoveNext()
{
bool result;
try
{
result = false;
}
catch (Exception exception)
{
<>1__state = -2;
<>t__builder.SetException(exception);
return;
}
<>1__state = -2;
<>t__builder.SetResult(result);
}
void IAsyncStateMachine.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
this.MoveNext();
}
[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
<>t__builder.SetStateMachine(stateMachine);
}
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
//ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
this.SetStateMachine(stateMachine);
}
}
[AsyncStateMachine(typeof(<Contains>d__0))]
public Task<bool> Contains(string key, string scope)
{
<Contains>d__0 stateMachine = default(<Contains>d__0);
stateMachine.<>t__builder = AsyncTaskMethodBuilder<bool>.Create();
stateMachine.<>1__state = -1;
AsyncTaskMethodBuilder<bool> <>t__builder = stateMachine.<>t__builder;
<>t__builder.Start(ref stateMachine);
return stateMachine.<>t__builder.Task;
}
Trying to pass function instead lambda expression and finally mixed up why line:
int t2 = await Task.Run( ()=>Allocate2() );
not raises error. This lambda expression ()=>Allocate2() not returns Task. Why no error?
How to create task without lambda expression with function Allocate?
static async void Example()
{
int t = await Task.Run(Allocate);
int t2 = await Task.Run( ()=>Allocate2() );
Console.WriteLine("Compute: " + t);
}
static Task<int> Allocate()
{
return 1;
}
static int Allocate2()
{
return 1;
}
Task.Run() wants you to pass a parameterless Action or Func to it.
A lambda can be assigned to an Action or a Func (as appropriate) which is why calling Task.Run() with a lambda works for you.
If you don't want to use a lambda, you must explicitly create an Action or a Func, passing the method you want to call to the constructor of the Action or the Func.
The following demonstrates:
static void Main()
{
var task = Task.Run(new Action(MyMethod));
}
static void MyMethod()
{
Console.WriteLine("MyMethod()");
}
OR:
static void Main()
{
var task = Task.Run(new Func<int>(MyMethod));
}
static int MyMethod()
{
Console.WriteLine("MyMethod()");
return 42;
}
Note that this doesn't work if the method needs one or more parameters. In that event, you must use a lambda.
public interface IBar {
}
public class Bar : IBar {
}
public class Bar2 : IBar {
}
public interface IFoo {
Task<T> Get<T>(T o) where T : IBar;
}
public class Foo : IFoo {
public async Task<T> Get<T>(T o) where T : IBar {
...
}
}
I can then call this method using reflection:
var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
var generic = method.MakeGenericMethod(bar2.GetType());
var task = generic.Invoke(foo, new [] { bar2 });
How do I await on this Task? and How do I cast it to Task<bar2.GetType()>?
Because Task<T> derives from Task you can await on just that, once the task is awaited you can use reflection to safely access the .Result property via reflection.
Once you have the result you will either need to store it in a IBar and use the methods and properties on that or cast to the specific type after testing to use the type specific methods.
Here is a full MCVE of it
using System;
using System.Reflection;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Test().Wait();
Console.ReadLine();
}
static async Task Test()
{
var foo = new Foo();
var bar2 = new Bar2();
object resultObject = await CallGetByReflection(foo, bar2);
IBar result = (IBar)resultObject;
result.WriteOut();
//or
if (resultObject is Bar)
{
((Bar)resultObject).Something();
}
else if (resultObject is Bar2)
{
((Bar2)resultObject).SomethingElse();
}
}
private static async Task<object> CallGetByReflection(IFoo foo, IBar bar)
{
var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
var generic = method.MakeGenericMethod(bar.GetType());
var task = (Task) generic.Invoke(foo, new[] {bar});
await task.ConfigureAwait(false);
var resultProperty = task.GetType().GetProperty("Result");
return resultProperty.GetValue(task);
}
public interface IBar
{
void WriteOut();
}
public class Bar : IBar
{
public void Something()
{
Console.WriteLine("Something");
}
public void WriteOut()
{
Console.WriteLine(nameof(Bar));
}
}
public class Bar2 : IBar
{
public void SomethingElse()
{
Console.WriteLine("SomethingElse");
}
public void WriteOut()
{
Console.WriteLine(nameof(Bar2));
}
}
public interface IFoo
{
Task<T> Get<T>(T o) where T : IBar;
}
public class Foo : IFoo
{
public async Task<T> Get<T>(T o) where T : IBar
{
await Task.Delay(100);
return o;
}
}
}
}
UPDATE: Here is a extension method to simplify the process
public static class ExtensionMethods
{
public static async Task<object> InvokeAsync(this MethodInfo #this, object obj, params object[] parameters)
{
var task = (Task)#this.Invoke(obj, parameters);
await task.ConfigureAwait(false);
var resultProperty = task.GetType().GetProperty("Result");
return resultProperty.GetValue(task);
}
}
This turns CallGetByReflection in to
private static Task<object> CallGetByReflection(IFoo foo, IBar bar)
{
var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
var generic = method.MakeGenericMethod(bar.GetType());
return generic.InvokeAsync(foo, new[] {bar});
}
UPDATE 2: Here is a new extension method that works with any awaitable type instead of only tasks by using dynamic and GetAwaiter()
public static class ExtensionMethods
{
public static async Task<object> InvokeAsync(this MethodInfo #this, object obj, params object[] parameters)
{
dynamic awaitable = #this.Invoke(obj, parameters);
await awaitable;
return awaitable.GetAwaiter().GetResult();
}
}
Based on your example you know type of returned object at compile time -> IFoo, so you can use normal casting (IFoo)
var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
var generic = method.MakeGenericMethod(typeof(IBar));
var task = (Task<IBar>)generic.Invoke(foo, new [] { bar2 });
IBar result = await task;
If you don't know a type at compile time, then use dynamic keyword
var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
var generic = method.MakeGenericMethod(bar2.GetType());
dynamic task = generic.Invoke(foo, new [] { bar2 });
IBar result = await task;
But if type of the task not a Task<iFoo> at runtime - exception will be thrown
And if you need concrete type of IBar then
var concreteResult = Convert.ChangeType(result, bar2.GetType());
On top of #ScottChamberlain answer (which is great 😉) I'll suggest a small improvement on InvokeAsync method to return Task<T> rather than Task<object>. Besides that it would be useful having a second method returning Task, which isn't supported by InvokeAsync.
using System.Threading.Tasks;
namespace System.Reflection
{
public static class MethodInfoExtensions
{
public static async Task<T> InvokeAsync<T>(this MethodInfo methodInfo, object obj, params object[] parameters)
{
dynamic awaitable = methodInfo.Invoke(obj, parameters);
await awaitable;
return (T)awaitable.GetAwaiter().GetResult();
}
public static async Task InvokeAsync(this MethodInfo methodInfo, object obj, params object[] parameters)
{
dynamic awaitable = methodInfo.Invoke(obj, parameters);
await awaitable;
}
}
}
Here is an easy and simple sample code
object[] param = {null};
var method = await (Task<bool>)typeof(YourClassName).GetMethod("YourMethodName",System.Reflection.BidingFlags.NonPublic | System.Reflection.BindingFlags.Instance).Invoke(InstanceOfYourClass, param);
you can use "result" property to avoid "await" keyword and should not decalre "async" in method.
var method = typeof(IFoo).GetMethod(nameof(IFoo.Get));
var generic = method.MakeGenericMethod(typeof(IBar));
var task = (Task<IBar>)generic.Invoke(foo, new [] { bar2 });
var resultProperty = task.GetProperty("Result");
var result = resultProperty.GetValue(task);
var convertedResult = Convert.ChangeType(result, bar2.GetType());
This would work if I remove Tuple and just using Task<bool> or Task<string>.
public async Tuple<Task<bool>, string> Test()
{
//....
return new Tuple<Task<bool>, string>(false, "a string");
}
Any idea how to make this work?
Async method should return void (bad practice) or Task or Task<T> (see Return Types section). In your case I believe it should be
public async Task<Tuple<bool, string>> Test()
{
// await ...
return Tuple.Create(false, "a string");
}