4

如何使用反射创建异步方法?

基本上我需要这样的东西:

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

但我需要通过反思来做到这一点。

4

3 回答 3

7

方法的翻译async工作在 C#(或 VB.NET)编译器级别,CIL 不支持它。编译器在最简单的情况下所做的就是翻译如下代码:

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

变成基本上等同于以下内容的代码:

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

真正的代码要复杂得多(它使用SynchronizationContext,如果something()返回已经完成Task,它实际上不是异步的,它支持await其他谢谢Task),对于更复杂的 C# 代码来说它会更加复杂。

但是,如果您真的想async使用 Reflection.Emit 创建方法,那么您实际上必须手动执行此转换。

但要注意的一点是,如果您的方法await在它之前只使用一次return,您可以将其重写为(假设something()返回 aTask而不是其他await可以的东西):

public Task asyncmethod()
{
    // some code
    return something();
}
于 2012-06-15T09:17:14.140 回答
1

解析您的问题的另一种方法是您需要在方法中使用反射(例如,访问私有方法),但仍想使用 await。

如果是这种情况,您可以像往常一样使用反射,取回任务(或任何可等待的东西),然后单独等待它。

就像是:

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

虽然,正如 svick 所提到的,如果唯一的等待是在最后,只需返回任务而不是将方法标记为异步并使用等待。

于 2012-06-15T09:43:28.340 回答
0

async/await 是编译器结构。它们在 CIL/ILGenerator/DynamicMethod 世界中不以这种形式存在。async 关键字和 await 运算符是更高级的语法糖果。async 的存在是为了标记一个允许您等待等待类型的方法。它们是实现 GetAwaiter() 的类型——一种返回实现 INotifyCompletion 接口的类型的方法——例如 Task/Task{T}——可以排队到 TaskScheduler。完成后将调用 INotifyCompletion.OnCompleted 延续。

如果您想通过 Reflection.Emit 动态生成“异步”方法,“所有”您所要做的就是将您想要在其中等待的任务返回到调用 DynamicMethod 的 C# 世界。然后,您可以通过 async/await 等待它。事情是这样的,你只能返回一个等待,所以当涉及到序列化等待时,例如:

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

除非可以为元组返回类型将多个等待对象推送到堆栈上(我不知道如何为元组值设置调用堆栈),然后调用 await result.Item1; 等待结果.Item2; ... await result.ItemN,或推送一个 List{Task/Task{T}},您必须通过 ContinueWith 将每个连续的可等待对象嵌套在每个先前的可等待对象延续中。我更喜欢返回 List{Task/Task{T}} 所涉及的所有分配,而不是简单地将 Task 引用推入堆栈。当谈到并行任务执行时,我不相信您可以避免 List{Task/Task{T}} 示例:

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

这意味着您必须通过 TypeBuilder 为每个 Task/Task{T}.ContinueWith 调用构建 lambda,使用 TypeBuilder.GetILGenerator() 动态生成它,然后继续将它们填充在父延续中,直到达到最顶层 ContinueWith - - 它返回一个您可以等待的任务 - 这就是您返回调用动态创建的“异步”方法的代码所需的全部内容。一旦先前的任务完成,这将导致每个任务连续运行。

于 2020-11-12T09:23:19.177 回答