首页 > 解决方案 > 如何在没有 Roslyn 的情况下将 Property1.Property2 之类的路径评估为已编译的表达式

问题描述

在我的 blazor webassembly 应用程序中,我想评估字符串路径,例如:

要求是:

  1. 我能够以某种方式编译/缓存表达式并使用不同的参数(参数=源对象)多次评估它。速度很重要
  2. 我需要评估路径以获得价值
  3. 如果路径指向属性,我需要能够设置值。
  4. 我需要能够获取 MemberInfo,尽管这可以使用不同的评估器在外部完成。

我不需要像这样评估数学表达式Property1 * Property2

我有什么

我使用过 RoslynAPI,类似于:

var discountFilter = "album => album.Quantity > 0";
var options = ScriptOptions.Default.AddReferences(typeof(Album).Assembly);
 
Func<Album, bool> discountFilterExpression = await CSharpScript.EvaluateAsync<Func<Album, bool>>(discountFilter, options);
 
var discountedAlbums = albums.Where(discountFilterExpression)

但我不能在我的 Blazor WebAssembly 应用程序中使用 Roslyn,因为它太重了。

是否有一种更轻量级的评估路径的方法,最好是构建表达式?

标签: c#evalblazor-webassembly

解决方案


我还没有专门为 WASM 测试过它,但是表达式树可能是你的朋友——但是,没有神奇的“解析这个字符串”按钮;但假设您有自己的字符串解析器,您将希望最终检查 AST 并构造Expression类似于:

// constructs: (Album album) => album.Quantity > 0
var p = Expression.Parameter(typeof(Album), "album");
var body = Expression.GreaterThan(
    Expression.PropertyOrField(p, "Quantity"),
    Expression.Constant(0, typeof(int)));
var lambda = Expression.Lambda<Func<Album, bool>>(body, p);

然后,您可以通过以下方式获取可重用委托:

Func<Album, bool> func = lambda.Compile();

在设置值方面:这也很好:Expression.Assign在一个Expression.Block可以再次编译的内部。

然而!在 WASM 上,我怀疑Compile()会使用解释而不是 ref-emit,这意味着它可能只是在底层使用反射(PropertyInfo.SetValue等),而不是任何更原生的东西。


推荐阅读