如何将Expression.Lambda附加到任何所有者类型?

时间:2016-07-12 22:02:52

标签: c# code-generation expression-trees reflection.emit dynamicmethod

我希望这个测试通过:

popen

这使得堆栈行走语言代码能够正常工作。什么是最简单的方法呢?

我更喜欢最便携的方式。

3 个答案:

答案 0 :(得分:3)

您可以在运行时构建新类型,然后将表达式编译为该类型的方法。

您需要在运行时创建新程序集和新模块。创建后,您可以使用它们创建任意数量的类型。以下是创建程序集和模块的代码示例:

var assemblyBuilder =
    AppDomain.CurrentDomain.DefineDynamicAssembly(
        new AssemblyName {Name = "MyNewAssembly"},
        AssemblyBuilderAccess.Run);

var moduleBuilder = assemblyBuilder.DefineDynamicModule("MyNewModule");

现在,您可以使用模块构建器来定义这样的新类型:

var typeBuilder = moduleBuilder.DefineType("MyNewType"); 

然后是一个像这样的新方法:

var methodBuilder = 
    typeBuilder.DefineMethod(
        "MyNewMethod",
        MethodAttributes.Public | MethodAttributes.Static,
        typeof(int), //returns an int
        new Type[]{}); //takes no parameters

请注意,方法签名应与您的表达式委托类型匹配。

接下来,我们使用CompileToMethod方法将表达式编译到新方法中:

var expression = Expression.Lambda(typeof(Func<int>), Expression.Constant(0));

expression.CompileToMethod(methodBuilder);

我们从类型构建器生成实际类型:

var type = typeBuilder.CreateType();

然后我们使用Delegate.CreateDelegate方法为新创建的静态方法创建委托,如下所示:

Func<int> func =
    (Func<int>)Delegate.CreateDelegate(
        typeof(Func<int>),
        type.GetMethod("MyNewMethod"));

int value = func(); //Test

现在func.Method.DeclaringType将返回我们动态创建的类型。

您可以轻松使用此代码生成一些辅助方法,以便于使用。

答案 1 :(得分:1)

好的,我自己找到了,但我不确定它在.NET Core中是如何工作的,哪个框架可能支持也可能不支持。如果您有更好(更优雅或更便携)的解决方案,请随时发布您的答案。

关键是使用CompileToMethod Lambda表达式。{/ p>

[Test]
public void LambdaTest2()
{
    var asm = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName("test"), AssemblyBuilderAccess.Run);
    var masm = asm.DefineDynamicModule("main");

    var type = masm.DefineType("TestType");
    var mb = type.DefineMethod("TestMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[0]);

    // your lambda
    ConstantExpression expressionTree = Expression.Constant(0);
    Expression.Lambda(typeof(Func<int>), expressionTree).CompileToMethod(mb);

    var m = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), type.CreateType().GetMethod("TestMethod"));

    Assert.That(m.Method.DeclaringType, Is.Not.Null);

    // you can create another in the same module but with another type (because type can't be changed)
    var type2 = masm.DefineType("TestType2");
    var mb2 = type2.DefineMethod("TestMethod2", MethodAttributes.Public | MethodAttributes.Static, typeof(int), new Type[0]);

    // your lambda 2
    ConstantExpression expresisonTree2 = Expression.Constant(1);
    Expression.Lambda(typeof(Func<int>), expresisonTree2).CompileToMethod(mb2);

    var m2 = (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), type2.CreateType().GetMethod("TestMethod2"));

    Assert.That(m2.Method.DeclaringType, Is.Not.Null);

    // check correctness
    Assert.That(m(), Is.EqualTo(0));
    Assert.That(m2(), Is.EqualTo(1));
}

答案 2 :(得分:0)

lambda表达式编译为DynamicMethod,对于DeclaringType属性,它始终为null。

请参阅DynamicMethod definition

请参阅this SO answer also

如果我能找到解决办法的话,会让我的生活更轻松。