Search code examples
c#code-generationexpression-treesreflection.emitdynamicmethod

How to attach Expression.Lambda to ANY owner type?


I want this test to pass:

[Test]
public void LambdaTest()
{
    var m = Expression.Lambda(typeof(Func<int>), Expression.Constant(0)).Compile();
    Assert.That(m.Method.DeclaringType, Is.Not.Null);
}

This is necessary to make stack-walking lagacy code to work correctly. What's the simpliest way to do it?

I would prefer the most portable way.


Solution

  • You can build a new type at runtime and then compile the expression into a method of that type.

    You need to create a new assembly and a new module at run time. Once you create those, you can use them to create as many types as you like. Here is a code sample to create the assembly and the module:

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

    Now, you can use the module builder to define a new type like this:

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

    And then a new method like this:

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

    Please note that the method signature should match your expression delegate type.

    Next, we compile the expression into the new method using the CompileToMethod method:

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

    We generate the actual type from the type builder:

    var type = typeBuilder.CreateType();
    

    Then we use the Delegate.CreateDelegate method to create a delegate to the newly created static method like this:

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

    Now func.Method.DeclaringType would return our dynamically created type.

    You can easily use this code to generate some helper methods to make it easy to use.