gpt4 book ai didi

.net - 如何使用 Reflection.Emit 注入(inject)文字表达式?

转载 作者:行者123 更新时间:2023-12-01 22:17:19 25 4
gpt4 key购买 nike

我正在开发一个项目,使用 C# 作为脚本语言来评估不同复杂度的标记化用户定义表达式。

我有一个使用 CodeDOM 和反射来生成评估器类、创建并加载程序集 (GenerateInMemory = true)、实例化该类并执行评估方法的工作模型。但是,我想将程序集加载到 AppDomain 中,以便在执行完成时卸载它。在研究这个问题时,我被引导到 AppDomain.DefineDynamicAssembly 方法。这似乎正是我所需要的,因为我可以创建一个可收藏的程序集。

以下是用户定义表达式的几个示例,以及我的 CodeDOM 项目生成的类:

简单的用户定义表达式:

return Abs(@HDL@/@LDL@ * 5.5);

生成的类:

namespace Lab.ResultProcessing
{

public sealed class ExpressionEvaluator
{
public double Evaluate()
{
return System.Math.Abs(449.86881550861/74.934407754305 * 5.5);
}
}
}

更复杂的用户定义表达式:

double GFR;
double MA_GFR;
double MB_GFR;
double FA_GFR;
double FB_GFR;

GFR = (170 *
Pow(@CREAT@, -0.999) *
Pow(@YEARS@, -0.176) *
Pow(@BUN@, -0.170) *
Pow(@ALBUMIN@, 0.318));

MA_GFR = GFR;
MB_GFR = GFR * 1.180;
FA_GFR = GFR * 0.762;
FB_GFR = GFR * 1.180 * 0.762;

if (("@RACE@" != "B") && ("@GENDER@" == "M"))
{
return MA_GFR;
}
else if (("@RACE@" == "B") && ("@GENDER@" == "M"))
{
return MB_GFR;
}
else if (("@RACE@" != "B") && ("@GENDER@" == "F"))
{
return FA_GFR;
}
else if (("@RACE@" == "B") && ("@GENDER@" == "F"))
{
return FB_GFR;
}
else
{
return GFR;
}

生成的类:

namespace Lab.ResultProcessing
{

public sealed class ExpressionEvaluator
{
public double Evaluate()
{
double GFR;
double MA_GFR;
double MB_GFR;
double FA_GFR;
double FB_GFR;

GFR = (170 *
System.Math.Pow(0.797258181752292, -0.999) *
System.Math.Pow(63.6814545438073, -0.176) *
System.Math.Pow(5.47258181752292, -0.170) *
System.Math.Pow(3.79725818175229, 0.318));

MA_GFR = GFR;
MB_GFR = GFR * 1.180;
FA_GFR = GFR * 0.762;
FB_GFR = GFR * 1.180 * 0.762;

if (("B" != "B") && ("M" == "M"))
{
return MA_GFR;
}
else if (("B" == "B") && ("M" == "M"))
{
return MB_GFR;
}
else if (("B" != "B") && ("M" == "F"))
{
return FA_GFR;
}
else if (("B" == "B") && ("M" == "F"))
{
return FB_GFR;
}
else
{
return GFR;
}
;
}
}
}

我现在尝试使用 Reflection.Emit 复制上述功能。我的问题是我还没有找到将去标记化公式注入(inject)到发出的类中的方法。

这是我正在使用的代码:

public static object DynamicEvaluate2(string expression)
{
AssemblyName assemblyName = new AssemblyName("Lab.ResultProcessing");
AppDomain appDomain = AppDomain.CurrentDomain;
AssemblyBuilder assemblyBuilder = appDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndCollect);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name);
TypeBuilder typeBuilder = moduleBuilder.DefineType("ExpressionEvaluator", TypeAttributes.Sealed);
MethodBuilder methodBuilder = typeBuilder.DefineMethod("Evaluate", MethodAttributes.Public | MethodAttributes.Final, typeof(double), null);
ILGenerator methodGenerator = methodBuilder.GetILGenerator();

methodGenerator.Emit(OpCodes.Ldobj, expression);
methodGenerator.Emit(OpCodes.Ret);

Type evaluatorType = typeBuilder.CreateType();
MethodInfo methodInfo = evaluatorType.GetMethod("Evaluate");

object evaluator = Activator.CreateInstance(evaluatorType);
object result = methodInfo.Invoke(evaluator, null);

return result;
}

当调用 methodInfo.Invoke 方法时,出现以下错误:

测试方法 ResultCalculatorTest.ResultCalculatorClassFactoryTest.DynamicEvaluate2Test 引发异常:System.Reflection.TargetInitationException:调用目标已引发异常。 ---> System.BadImageFormatException:错误的类标记。

所以我有几个问题:

如何使用 Reflection.Emit 注入(inject)去标记化的用户定义表达式?
有没有办法查看发出的类的 C# 代码,还是只能在 IL 中?
如何调试发出的类?

任何帮助将不胜感激。

最佳答案

methodGenerator.Emit(OpCodes.Ldobj, expression);

这并没有达到您想要的效果:ldobj 指令需要一个Type,而不是一个string。根据 MSDN,ldobj 指令的目的是 copy the value type object pointed to by an address .

与 CodeDom 不同,Reflection.Emit 不会为您解析表达式。您的代码需要解析表达式字符串并发出正确的 IL 操作码序列来计算该表达式。实际上,您需要编写自己的编译器。

Reflection.Emit 的替代方案是 System.Linq.Expressions 中的类型。它们的级别高于 Reflection.Emit,但低于 CodeDom。您仍然需要解析字符串,但您将在内存中构建抽象语法树,而不是发出原始操作码。

关于.net - 如何使用 Reflection.Emit 注入(inject)文字表达式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3522876/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com