// Build a normal expression tree with "await" calls
var paraInput = Expression.Parameter(typeof(string), "input");
var exprTree = Expression.Lambda<Func<Task<string>, string>>(
Expression.Block(
Expression.Call(typeof(Task), nameof(Task.Delay), null, Expression.Constant(1000)).Await(false),
paraInput.Await(false)),
paraInput);
// Create compilable state machine async expression tree (result must be Task<?> or Task)
var asyncExprTree = exprTree.Async<Func<Task<string>, Task<string>>>();
var asyncCompiled = asyncExprTree.Compile();
// Invoke delegate as usual
var result = await asyncCompiled(Task.FromResult("test")).ConfigureAwait(false);
2条答案
按热度按时间83qze16e1#
await
涉及重大编译器重写;生成的IL与原始的C#非常不同,具有可变提升(到类上)和分支、任务、延续等。它肯定不是可以用 * 简单 * lambda表示的东西,尽管在最近版本的.NET中具有更丰富的Expression
支持(Expression.Block
等),* 技术上 * 我想模仿编译器为await
所做的大多数事情是可能的--但是你可能会非常愚蠢地试图手工完成它。不,AFAIK,
Expression
API中不存在 * 自动化 * 这种转换的工具,坦率地说,我从来没有期望过有。ILGenerator
大概也是如此;坦率地说,AFAIK在元编程中使用await
的唯一“简单”方法(我使用“简单”一词是不正确的)是生成C#并通过roslyn或CSharpCodeProvider
运行它。sc4hvdpw2#
派对迟到了,但我刚出版了一本书,确实做到了这一点。
https://github.com/avonwyss/bsn.AsyncLambdaExpression
它利用了表达式树中嵌套的lambda可以访问外部lambda变量这一事实(它们被捕获为闭包,就像嵌套lambda在C#中的行为一样)。因此,代码创建了包含所有必要变量并返回
Task
的主lambda,以及嵌套lambda中的状态机,该状态机可作为awaiter.OnComplete()
上的延续来调用。当前等待者等被存储在闭包中,则即使当内部lambda停止执行时,状态机也保持其状态。使用它的API包含两个扩展方法,一个用于wait,另一个用于将带有wait的表达式树转换为实现状态机并返回
Task
的lambda。下面是一个代码示例:
原始lambda表达式树的DebugView如下所示:
异步转换创建以下表达式树:
这是一个简单的例子;但是异步转换不仅可以处理await,而且可以处理控制流表达式(循环、条件、标签/转到、try..catch..finally),以便提供对各种表达式树的支持。