1

我有一个使用 ILEmit 生成 DynamicMethod 的方法,我想将其内容内联到表达式树中。我需要这样做才能将表达式树写入程序集。

我可以编译 DynamicMethod 并将其包含到树中,但这会阻止我将表达式树写入程序集,并出现以下错误:

CompileToMethod 无法编译常量“BulkUtil+BlitMethod[Byte]”,因为它是一个重要的值,例如一个活动对象。相反,创建一个可以构造该值的表达式树。

由于无论如何我都在生成方法的主体,我想我只是将它内联到树中以便它可以输出。但是,由于该方法会生成不安全的代码,我不知道如何直接翻译它。

有没有办法直接将 IL 添加到表达式树?

4

2 回答 2

2

我认为尝试将 IL 内联到表达式树中不是正确的方法,因为这意味着您必须知道从表达式生成的 IL 的样子(至少在某种程度上)。此外,据我所知,没有简单的方法可以做到这一点。

相反,您可以做的是MethodBuilder在生成的程序集中将您的 IL 方法创建为普通方法(即 not DynamicMethod),然后从表达式中调用该方法。

带有非常简单的 IL 和表达式的代码可能如下所示:

var assembly = AssemblyBuilder.DefineDynamicAssembly(
    new AssemblyName("a"), AssemblyBuilderAccess.RunAndSave);
var module = assembly.DefineDynamicModule("a.dll");

var ilType = module.DefineType("IlType");
var ilMethod = ilType.DefineMethod(
    "M", MethodAttributes.Public | MethodAttributes.Static,
    typeof(int), Type.EmptyTypes);
var il = ilMethod.GetILGenerator();
il.Emit(OpCodes.Ldc_I4, 42);
il.Emit(OpCodes.Ret);
var createdIlType = ilType.CreateType();

var expressionType = module.DefineType("ExpressionType");
var expressionMethod = expressionType.DefineMethod(
    "M", MethodAttributes.Public | MethodAttributes.Static,
    typeof(int), Type.EmptyTypes);
var lambda = Expression.Lambda<Func<int>>(
    Expression.Call(createdIlType.GetMethod("M")));
lambda.CompileToMethod(expressionMethod);
expressionType.CreateType();

assembly.Save("a.dll");

似乎Expression不能MethodBuilder直接调用,这意味着这两个方法必须在不同的类型中,并且必须首先创建包含IL方法的类型。

于 2013-11-04T11:10:09.677 回答
1

不幸的是,写入方法的表达式树非常有限。您只能编写静态方法,它们只能引用已经物化的类或方法。(即没有建设者)。

在我完全放弃这个想法之前,我尝试了创建“存根”静态和实例方法的方法,这将调用具有相同名称的被调用者的所有参数的委托。
例如

public class MyClass<Proxy>
{
      static Func<MyClass,int,float,long,double,object> <Delegate>Foo
      public override object Foo(int a,float b,long c,double d){
          return MyClass_Proxy.<Delegate>Foo.Invoke(this,a,b,c,d)
      }
}

这样,由于所有内容都已物化,因此表达式树可以引用所有成员。这是一个可怕的 hack,它成长为一个动态代理。在您使用泛型方法或泛型类方法之前,这很有效(我什至会说得很好),然后它就落在了它的脸上。

您可以在这里查看我对这些代理的看法: https: //bitbucket.org/mburbea/delegatedproxy/src ProxiedTypeBuilder 和 ProxiedMethodBuilder 完成了基于类或接口创建代理类的大部分艰苦工作。我的问题表面有点严重,因为我需要我的代理方法能够调用它们的基本实现(这是大多数丑陋的代码要做的事情),我想出了一种适合我需要处理的方法具有泛型参数的方法。

于 2013-11-25T20:16:39.550 回答