我现在正在移植一些使用表达式到.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
- &gt;将MethodInfo
绑定到特定TypeBuilder
,而不是在其上构建自己DynamicMethod
。
答案 0 :(得分:6)
这不是一个理想的解决方案,但如果您不想从头开始编写所有内容,那么值得考虑:
CompileToMethod
实施,您会看到它使用内部LambdaCompiler
类。LambdaCompiler
使用System.Reflection.Emit
将lambda转换为MethodInfo
。System.Reflection.Emit
受.NET Core支持。
LambdaCompiler
源代码。你可以找到它here。此解决方案的最大问题是:
LambdaCompiler
分布在许多文件中,因此找到编译它所需的内容可能很麻烦。LambdaCompiler
可能会使用.NET Core不支持的某些API。另外一些评论:
答案 1 :(得分:1)
将一些代码移植到netstandard时遇到了同样的问题。我的解决方案是使用Compile方法将lambda编译为Func,将Func存储在我添加到动态类型的静态字段中,然后在我的动态方法中,我只需从该静态字段加载并调用Func。这允许我使用LINQ Expression API而不是反射发射来创建lambda(这本来很痛苦),但仍然让我的动态类型实现一个接口(这是我的场景的另一个要求)。
感觉有点像黑客,但它有效,并且可能比通过LambdaCompiler重新创建CompileToMethod功能更容易。
答案 2 :(得分:1)
基于Michal Komorowski的回答,我决定尝试将LambdaCompiler
移植到.NET Core。您可以找到我的努力here(GitHub链接)。实际上,该类分布在多个文件中的事实是这里最小的问题之一。更大的问题是它依赖于.NET Core代码库中的内部部分。
从上面的GitHub存储库中引用自己:
不幸的是,由于(至少)以下问题,它是不平凡的:
AppDomain.CurrentDomain.DefineDynamicAssembly在.NET Core中不可用-AssemblyBuilder.DefineDynamicAssembly替换为is。这样的答案说明了如何使用它。
Assembly.DefineVersionInfoResource不可用。
依赖于内部方法和属性,例如BlockExpression.ExpressionCount,BlockExpression.GetExpression,BinaryExpression.IsLiftedLogical等。
由于上述原因,尝试将其作为独立程序包进行工作本质上是徒劳的。使这项工作唯一可行的方法就是将其包含在.NET Core中。
但是,由于其他原因,这是有问题的。我相信许可是这里的绊脚石之一。其中一些代码可能是DLR工作的一部分,而DLR工作本身就是Apache许可的。一旦您在代码库中获得了第三方贡献者的贡献,(或与.NET Core代码库的其余部分一样)将其重新许可给MIT便变得或多或少了。
我认为,根据使用情况,目前最好的选择是以下两种方式之一:
使用DLR (Dynamic Language Runtime)(可从NuGet(Apache 2.0许可)获得)。这是赋予IronPython的运行时,据我所知,这是唯一的一种主动维护的,由DLR支持的语言。 (IronRuby和IronJS似乎都被放弃了。)DLR允许您使用Microsoft.Scripting.Ast.LambdaBuilder
定义lambda表达式; IronPython似乎并没有直接使用它。还有一个Microsoft.Scripting.Interpreter.LightCompiler
类似乎很有趣。
不幸的是,DLR的文献似乎很少。我认为有一个the CodePlex site所引用的Wiki,但它是脱机的(虽然可以通过将代码下载到CodePlex上来进行访问)。
使用Roslyn为您编译(动态)代码。这可能也有一些学习曲线。不幸的是,我本人对此并不十分熟悉。
这似乎有很多策划的链接,教程等:https://github.com/ironcev/awesome-roslyn。我建议将此作为起点。如果您对动态构建方法特别感兴趣,那么这些也值得一读:
以下是罗斯林的其他一些一般阅读链接。不过,其中大多数链接都集中在分析 C#代码上(这是Roslyn的用例之一),但是Roslyn可用于生成 IL代码(即“编译”)以及C#代码。
还有第三种选择,对我们大多数人来说可能不感兴趣: