Asynchronous programming with reflection
Asked Answered
N

3

5

How can I create an asynchronous method using reflection?

Basically I need something like this:

async public Task asyncmethod()
{
   await something();
}

but I need to do it with reflection.

Nabokov answered 15/6, 2012 at 8:17 Comment(6)
Why use async/await at all? If something() returns a task, just call Wait on it.Punitive
@PanagiotisKanavos Because that would make the method synchronous, avoiding that is the whole point of async.Carliecarlile
you can't create methods by reflection, you can call themHillard
@sleimanjneidi You can, using Reflection.Emit.Carliecarlile
@svick, there is already an await in there which means that he wants to wait on something. What I meant was why use async/await at all, just create a task, initialize it with the proper call, start it and work with it. Since there is no result to return, the only thing you can do is call Wait() or ContinueWith() on it at some pointPunitive
@PanagiotisKanavos, there is no await in there. He wants to dynamically create such method at runtime. The asyncmethod doesn't exist at compile-time.Photomap
C
7

The translation of async methods works at the level of the C# (or VB.NET) compiler, there is no support for it in CIL. What the compiler does in the simplest case is that it translates code like this:

async public Task asyncmethod()
{
    // some code
    var x = await something();
    // more code
}

into code that is basically equivalent to something like:

public Task asyncmethod()
{
   // some code
   return something().ContinueWith(
       t =>
       {
           var x = t.Result;
           // more code
       });
}

The real code is much more complicated that that (it uses SynchronizationContext, it's not actually asynchronous if something() returns already finished Task, it supports awaiting things other thank Tasks) and it would be even more complicated for more complicated C# code.

But if you really want to create async methods using Reflection.Emit, this transformation is what you actually have to do manually.

But one thing to note is that if your method uses await only once right before it returns, you can rewrite it into (assuming something() returns a Task and not something else that is awaitable):

public Task asyncmethod()
{
    // some code
    return something();
}
Carliecarlile answered 15/6, 2012 at 9:17 Comment(3)
can you pleaase tell where i can find more about how the compiler translates the code with async? I would like to await to things other than Task even if the code would be more complicatedNabokov
I'm not sure there is a single definitive source that describes the current situation. The best option would be C# 5.0 spec, but that hasn't been released yet. There is a specification for async, a series of articles by Eric Lippert and another series of articles by Jon Skeet.Carliecarlile
But most of them talk about the CTPs and the situation changed somewhat since then. Although parts 18 and 20 of Jon Skeet's series talk about the changes between CTP and DP and between DP and beta, respectively.Carliecarlile
A
1

The other way to parse your question is that you need to use reflection within the method (for instance, to access a private method), but still want to use await.

If that's the case, you can just use reflection like you normally would, get the Task (or whatever awaitable thing) back, then await it separately.

Something like:

public async Task asyncmethod()
{
    Task t = ... // code that fetches the Task via reflection
    await t;
}

although, as svick mentions, if the only await is at the end, just return the Task instead of marking the method async and using await.

Agitation answered 15/6, 2012 at 9:43 Comment(0)
E
0

async/await are compiler constructs. they do not exist in that form in the the CIL/ILGenerator/DynamicMethod world. the async keyword and await operator are higher level syntactical candy. async exists to mark a method which allows you to await on awaitable types. which are types that implement a GetAwaiter() -- a method that returns a type that implements the INotifyCompletion interface -- such as Task/Task{T} -- that can be queued to a TaskScheduler. which will call the INotifyCompletion.OnCompleted continuation when it's completed.

if you want to dynamically generate an "async" method via Reflection.Emit, "all" you have to do is just return the task you want to await on inside of it, back to your C# world that is calling the DynamicMethod. then, you can await on it via async/await. here's the thing though, you can only return one awaitable, so when it comes to serializing awaitables, e.g:

await firstTask;
await secondTask;
await thirdTask;
// ...
await nTask;

unless it's possible to push multiple awaitables onto the stack for a tuple return type(i don't know how to set the callstack up for a tuple value), and then call await result.Item1; await result.Item2; ... await result.ItemN, or push a List{Task/Task{T}}, you would have to nest each successive awaitable inside each antecedent awaitables continuation via ContinueWith. which i would prefer over all of the allocation involved with returning a List{Task/Task{T}} versus simply pushing Task references onto the stack. when it comes to parallel task execution, i don't believe you can avoid the List{Task/Task{T}} example:

Task DynamicQuoteAsyncUnquoteMethod()
{
    return firstAwaitedAsyncMethod()
        .ContinueWith(
            task =>
            {
                secondAwaitedAsyncMethod()
                    .ContinueWith(
                        task =>
                        {
                            thirdAwaitedAsyncMethod()
                                .ContinueWith(
                                task =>
                                {
                                    // yatta yatta
                                }
                        }
            }
        );
}

which means you would have to build the lambdas for each Task/Task{T}.ContinueWith call via TypeBuilder, generate it with TypeBuilder.GetILGenerator() dynamically, and then keep stuffing them inside parent continuations until you reach the top level most ContinueWith -- which returns a Task that you can await on -- that's all you need to return to the code that is calling the dynamically created "async" method. which will result in each task being ran in succession once the previous tasks completes.

Echinoid answered 12/11, 2020 at 9:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.