Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic Methods with Expression Trees: Simple property assignment fails

Tags:

c#

lambda

Following code to do a simple property assignment person.Name = "Joe Bloggs" by dynamically creating a method using an Expression Tree fails with a NullReferenceException - it's like the person parameter I create isn't being passed in.

Any ideas?

class Person
{
    public string Name { get; set; }
}

static void ExpressionTest()
{
    var personParam = Expression.Parameter(typeof(Person), "person");

    var block = Expression.Block(new[] { personParam },
        Expression.Assign(
            Expression.Property(personParam, "Name"), Expression.Constant("Joe Bloggs"))
        );
    /* 
        * block.DebugView in debugger shows:
        * 
        *   .Block(MyProject.MyNamepace.Person $person) {
        *       $person.Name = "Joe Bloggs"
        *   }
        *   
        */

    var method = Expression.Lambda<Action<Person>>(block, personParam).Compile();

    var person = new Person();

    method(person); // **throws** System.NullReferenceException: ... at lambda_method(Closure , Person )

    Debug.WriteLine(person.Name); // I expect this to print "Joe Bloggs"
}

Update

Thanks for the great answers, I saw @decPL's first that led me to removing the new[] { personParam } from the Expression.Block call.

It all makes complete sense that a Block scopes variables and you need to define them first (like some languages force you to) - my problem was that I didn't need any variables but was led astray by the magic DebugView property that the debugger shows you into thinking that they were parameters, and the Block was like a function definition:

.Block(MyProject.MyNamepace.Person $person) {
    $person.Name = "Joe Bloggs"
}

... which of course it isn't. It's a block of code as the name suggests duh!

like image 561
Duncan Smart Avatar asked Nov 08 '13 14:11

Duncan Smart


3 Answers

To clarify, BlockExpression represents a sequence of expressions that are executed sequentially, and returns the value of the last expression.

The version of Expression.Block you are using "creates a BlockExpression that contains the given variables and expressions." (http://msdn.microsoft.com/en-us/library/dd324074(v=vs.110).aspx) hence it creates code:

{
    Person person;
    person.Name = "Joe Bloggs";
}

So it clearly throws NullReferenceException.

like image 183
Konrad Kokosa Avatar answered Nov 01 '22 08:11

Konrad Kokosa


It works as expected if you remove the block:

var personParam = Expression.Parameter(typeof(Person), "person");
var method = Expression.Lambda<Action<Person>>(Expression.Assign(
                    Expression.Property(personParam, "Name"), Expression.Constant("Joe Bloggs")), personParam).Compile();

to fix your original code, you should remove the parameter expression passed to the block:

var block = Expression.Block(
                Expression.Assign(
                    propertyExpr, Expression.Constant("Joe Bloggs"))
                );
like image 4
Lee Avatar answered Nov 01 '22 08:11

Lee


You're passing the parameter of your method to the Expression.Block method as a variable, which it is not. If you remove it, it will work correctly:

var block = Expression.Block(
               Expression.Assign(
                   Expression.Property(personParam, "Name"),
                   Expression.Constant("Joe Bloggs")));

Of course, if that's the extent of your program, you may just as well drop the block expression.

like image 1
decPL Avatar answered Nov 01 '22 06:11

decPL