如何使用反射创建异步方法?
基本上我需要这样的东西:
async public Task asyncmethod()
{
await something();
}
但我需要通过反思来做到这一点。
如何使用反射创建异步方法?
基本上我需要这样的东西:
async public Task asyncmethod()
{
await something();
}
但我需要通过反思来做到这一点。
方法的翻译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();
}
解析您的问题的另一种方法是您需要在方法中使用反射(例如,访问私有方法),但仍想使用 await。
如果是这种情况,您可以像往常一样使用反射,取回任务(或任何可等待的东西),然后单独等待它。
就像是:
public async Task asyncmethod()
{
Task t = ... // code that fetches the Task via reflection
await t;
}
虽然,正如 svick 所提到的,如果唯一的等待是在最后,只需返回任务而不是将方法标记为异步并使用等待。
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 - - 它返回一个您可以等待的任务 - 这就是您返回调用动态创建的“异步”方法的代码所需的全部内容。一旦先前的任务完成,这将导致每个任务连续运行。