我可以使用System.Linq.Expressions动态生成异步方法吗?

e0bqpujr  于 2023-01-22  发布在  其他
关注(0)|答案(2)|浏览(100)

我知道编译器不能将异步lambda表达式转换为表达式树,但是可以手动生成表达式树吗?

var expr = Expression.Lambda<Func<Task>>(
     // how do I use 'await' in the body here?
);
var func = expr.Compile();

我在Expression类中找不到任何与asyncawait相关的方法,但也许还有其他方法?

83qze16e

83qze16e1#

await涉及重大编译器重写;生成的IL与原始的C#非常不同,具有可变提升(到类上)和分支、任务、延续等。它肯定不是可以用 * 简单 * lambda表示的东西,尽管在最近版本的.NET中具有更丰富的Expression支持(Expression.Block等),* 技术上 * 我想模仿编译器为await所做的大多数事情是可能的--但是你可能会非常愚蠢地试图手工完成它。
不,AFAIK,Expression API中不存在 * 自动化 * 这种转换的工具,坦率地说,我从来没有期望过有。
ILGenerator大概也是如此;坦率地说,AFAIK在元编程中使用await的唯一“简单”方法(我使用“简单”一词是不正确的)是生成C#并通过roslyn或CSharpCodeProvider运行它。

sc4hvdpw

sc4hvdpw2#

派对迟到了,但我刚出版了一本书,确实做到了这一点。
https://github.com/avonwyss/bsn.AsyncLambdaExpression
它利用了表达式树中嵌套的lambda可以访问外部lambda变量这一事实(它们被捕获为闭包,就像嵌套lambda在C#中的行为一样)。因此,代码创建了包含所有必要变量并返回Task的主lambda,以及嵌套lambda中的状态机,该状态机可作为awaiter.OnComplete()上的延续来调用。当前等待者等被存储在闭包中,则即使当内部lambda停止执行时,状态机也保持其状态。
使用它的API包含两个扩展方法,一个用于wait,另一个用于将带有wait的表达式树转换为实现状态机并返回Task的lambda。
下面是一个代码示例:

// 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);

原始lambda表达式树的DebugView如下所示:

.Lambda #Lambda1<System.Func`2[System.Threading.Tasks.Task`1[System.String],System.String]>(System.Threading.Tasks.Task`1[System.String] $input)
{
    .Block() {
        .Call bsn.AsyncLambdaExpression.AsyncExpressionExtensions.AwaitVoid(.Call (.Call System.Threading.Tasks.Task.Delay(1000)).ConfigureAwait(False));
        .Call bsn.AsyncLambdaExpression.AsyncExpressionExtensions.AwaitResult(.Call $input.ConfigureAwait(False))
    }
}

异步转换创建以下表达式树:

.Lambda #Lambda1<System.Func`2[System.Threading.Tasks.Task`1[System.String],System.Threading.Tasks.Task`1[System.String]]>(System.Threading.Tasks.Task`1[System.String] $input)
{
    .Block(
        System.Runtime.CompilerServices.ConfiguredTaskAwaitable+ConfiguredTaskAwaiter $awaiter,
        System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1+ConfiguredTaskAwaiter[System.String] $awaiter,
        System.Int32 $state,
        System.Int32 $resumeState,
        System.Threading.Tasks.TaskCompletionSource`1[System.String] $taskCompletionSource,
        System.Action $continuation,
        System.Exception $exception) {
        $taskCompletionSource = .New System.Threading.Tasks.TaskCompletionSource`1[System.String](.Constant<System.Threading.Tasks.TaskCreationOptions>(RunContinuationsAsynchronously));
        $continuation = .Lambda #Lambda2<System.Action>;
        .Invoke $continuation();
        $taskCompletionSource.Task
    }
}

.Lambda #Lambda2<System.Action>() {
    .Try {
        .Loop  {
            .Switch ($state) {
            .Case (0):
                    .Block() {
                        $state = 1;
                        .If (
                            !($awaiter = .Call (.Call (.Call System.Threading.Tasks.Task.Delay(50)).ConfigureAwait(False)).GetAwaiter()).IsCompleted
                        ) {
                            .Block() {
                                .Call $awaiter.OnCompleted($continuation);
                                .Break :break { }
                            }
                        } .Else {
                            .Default(System.Void)
                        }
                    }
            .Case (1):
                    .Block() {
                        $state = 2;
                        .Call $awaiter.GetResult();
                        .If (
                            !($awaiter = .Call (.Call $input.ConfigureAwait(False)).GetAwaiter()).IsCompleted
                        ) {
                            .Block() {
                                .Call $awaiter.OnCompleted($continuation);
                                .Break :break { }
                            }
                        } .Else {
                            .Default(System.Void)
                        }
                    }
            .Case (2):
                    .Block(System.String $result:2) {
                        $result:2 = .Call $awaiter.GetResult();
                        .Call $taskCompletionSource.SetResult($result:2);
                        .Break :break { }
                    }
            .Default:
                    .Throw .New System.InvalidOperationException()
            }
        }
        .LabelTarget :break:
    } .Catch (System.Exception $ex) {
        .Call $taskCompletionSource.SetException($ex)
    }
}

这是一个简单的例子;但是异步转换不仅可以处理await,而且可以处理控制流表达式(循环、条件、标签/转到、try..catch..finally),以便提供对各种表达式树的支持。

相关问题