【问题标题】:Alternatives of CompileToMethod in .Net Standard.Net Standard 中 CompileToMethod 的替代方案
【发布时间】:2017-01-07 10:20:18
【问题描述】:

我现在正在将一些使用表达式的库移植到 .Net Core 应用程序,并遇到了一个问题,即我的所有逻辑都基于 LambdaExpression.CompileToMethod,而这只是缺少。这里是示例代码:

public static MethodInfo CompileToInstanceMethod(this LambdaExpression expression, TypeBuilder tb, string methodName, MethodAttributes attributes)
{
    ...

    var method = tb.DefineMethod($"<{proxy.Name}>__StaticProxy", MethodAttributes.Private | MethodAttributes.Static, proxy.ReturnType, paramTypes);
    expression.CompileToMethod(method);

    ...
}

是否有可能以某种方式重写它以使使用表达式生成方法成为可能?我已经可以使用 Emit 来完成它,但它非常复杂,我想避免它以支持高级表达式。

我尝试使用var method = expression.Compile().GetMethodInfo();,但在这种情况下出现错误:

System.InvalidOperationException : 无法导入全局方法或 来自不同模块的字段。

我知道我可以手动发出 IL,但我需要将 Expression -> 完全转换为绑定到特定 TypeBuilderMethodInfo,而不是在其上构建自己的 DynamicMethod

【问题讨论】:

  • 哇,试图回答这个问题,我刚刚发现 .Net 核心缺少文档,并且没有简单的方法来找出替换了什么......祝你好运进入那个端口......
  • @LucasCorsaletti 我认为提供这样的文档非常困难,因为我想即使是 .net 核心团队也不知道什么被什么取代了 :) 无论如何,谢谢你的尝试
  • 有趣,我正在看这个问题。最近刚刚尝试使用 Linq.Expressions 和旧的 .NET 4.5 创建类/实例方法。对于可见性属性,动态方法可以创建为实例方法,但需要 Reflection.Emit。使用 Linq.Expressions 构建它更具可读性,但它不能用作实例方法。但是,我可以无耻地访问内部和私有,从而实现与实例方法相同的功能。我只是运行时代码生成的初学者...
  • @ErikHart 我有一个解决方法,例如方法,see link。因此,上面的代码片段来自这个解决方法,它停止迁移到 .Net Core :)

标签: c# .net .net-core .net-standard


【解决方案1】:

这不是一个理想的解决方案,但如果您不想从头开始编写所有内容,则值得考虑:

  1. 如果您查看 CompileToMethod 实现,您会发现它使用内部 LambdaCompiler 类。
  2. 如果深入挖掘,您会发现LambdaCompiler 使用System.Reflection.Emit 将lambda 转换为MethodInfo
  3. .NET Core 支持System.Reflection.Emit
  4. 考虑到这一点,我的建议是尝试重用LambdaCompiler 源代码。你可以找到它here

这个解决方案最大的问题是:

  1. LambdaCompiler 分布在许多文件中,因此查找编译它所需的文件可能很麻烦。
  2. LambdaCompiler 可能使用了一些 .NET Core 根本不支持的 API。

一些额外的 cmets:

  1. 如果您想查看哪个平台支持哪个 API,请使用 .NET API Catalog
  2. 如果您想查看 .NET 标准版本之间的差异,请使用 this 站点。

【讨论】:

  • 我在问问题之前反编译了CompileToMethod,但是当我发现LambdaCompiler 是内部的时我停止了。但是,现在我有了一个想法,我们可以通过反射调用一些内部方法。 不,迅猛龙,不要吃我 (you got the joke)。 如果没有其他人愿意帮助我们,我会进行一些研究并返回结果并将其标记为答案。
  • 所以我来了。由于未知原因.Net Standard 缺少构造函数LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda, MethodBuilder method)。没有理由不提供它,因为MethodBuilder 在另一个重载中使用,因此引用了每个必需的程序集。我想我应该在某个存储库(corefx?)上发布一个问题。目前我想创建一个拉取请求来解决这个问题,但不确定 repo。
  • 查看corefx 来源后,我发现这是故意使用FEATURE_COMPILE_TO_METHODBUILDER 标志完成的。但是不知道为什么。
【解决方案2】:

免责声明:我是图书馆的作者。

我已经创建了 Expression Compiler,它的 API 与 Linq Expressions 的 API 相似,只是略有改动。 https://github.com/yantrajs/yantra/wiki/Expression-Compiler

var a = YExpression.Parameter(typeof(int));
var b = YExpression.Parameter(typeof(int));

var exp = YExpression.Lambda<Func<int,int,int>>("add",
            YExpression.Binary(a, YOperator.Add, b),
            new YParameterExpression[] { a, b });

var fx = exp.CompileToStaticMethod(methodBuilder);

Assert.AreEqual(1, fx(1, 0));
Assert.AreEqual(3, fx(1, 2));

这个库是我们创建的 JavaScript 编译器的一部分,它是在 LGPL 下发布的。我们正在积极开发它,并在 JavaScript 中添加了生成器和异步/等待功能,因此您可以创建可调试的 JavaScript 代码并在其中轻松运行 C# 代码,而不是使用 Expression Compiler。

【讨论】:

    【解决方案3】:

    我在将一些代码移植到 netstandard 时遇到了同样的问题。我的解决方案是使用 Compile 方法将 lambda 编译为 Func,将 Func 存储在我添加到动态类型的静态字段中,然后在我的动态方法中,我只需从该静态字段加载和调用 Func。这使我可以使用 LINQ 表达式 API 而不是反射发射来创建 lambda(这会很痛苦),但仍然让我的动态类型实现一个接口(这是我的场景的另一个要求)。

    感觉有点像 hack,但它有效,并且可能比尝试通过 LambdaCompiler 重新创建 CompileToMethod 功能更容易。

    【讨论】:

      【解决方案4】:

      尝试让 LambdaCompiler 在 .NET Core 上运行

      基于 Michal Komorowski 的回答,我决定尝试将 LambdaCompiler 移植到 .NET Core。你可以找到我的努力here(GitHub 链接)。类分布在多个文件中的事实老实说是这里最小的问题之一。更大的问题是它依赖于 .NET Core 代码库中的内部部分。

      从上面的 GitHub 存储库中引用我自己的话:

      不幸的是,由于(至少)以下问题,这并不重要:

      • AppDomain.CurrentDomain.DefineDynamicAssembly 在 .NET Core 中不可用 - AssemblyBuilder.DefineDynamicAssembly 替换为。这个 SO 答案描述了如何使用它。

      • Assembly.DefineVersionInfoResource 不可用。

      • 依赖于内部方法和属性,例如 BlockExpression.ExpressionCount、BlockExpression.GetExpression、BinaryExpression.IsLiftedLogical 等。

      由于上述原因,试图将其作为独立包工作的尝试本质上是徒劳的。实现这项工作的唯一现实方法是将其正确包含在 .NET Core 中。

      但是,由于其他原因,这存在问题。我相信许可是这里的绊脚石之一。其中一些代码可能是作为 DLR 工作的一部分编写的,它本身已获得 Apache 许可。一旦您在代码库中获得了来自第 3 方贡献者的贡献,重新授权它(与 .NET Core 代码库的其余部分一样,向 MIT 授权)或多或少是不可能的。

      其他选项

      根据用例,我认为目前您最好的选择是以下两者之一:

      还有第三种选择,可能我们大多数人都不感兴趣:

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2017-04-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多