表达式树的动态方法:简单的属性赋值失败

时间:2013-11-08 14:04:07

标签: c# lambda

通过使用表达式树动态创建方法来执行简单的属性赋值person.Name = "Joe Bloggs"以及NullReferenceException失败 - 就像我创建的person参数没有被传递一样英寸

有什么想法吗?

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"
}

更新

感谢您提供了很好的答案,我首先看到@ decPL导致我从new[] { personParam }电话中删除了Expression.Block

这一切都是完全合理的,Block范围变量,你需要先定义它们(就像某些语言强迫你) - 我的问题是我不需要任何变量但被误入歧途调试器向您显示的魔术DebugView属性,认为它们是参数,而Block就像函数定义:

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

......当然不是。这是代码的,顾名思义就是duh!

3 个答案:

答案 0 :(得分:8)

为了阐明,BlockExpression表示按顺序执行的一系列表达式,并返回最后一个表达式的值。

您正在使用的Expression.Block版本“创建一个包含给定变量和表达式的BlockExpression。” (http://msdn.microsoft.com/en-us/library/dd324074(v=vs.110).aspx)因此它创建了代码:

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

所以它显然会抛出NullReferenceException。

答案 1 :(得分:4)

如果删除块,它会按预期工作:

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();

要修复原始代码,您应该删除传递给块的参数表达式:

var block = Expression.Block(
                Expression.Assign(
                    propertyExpr, Expression.Constant("Joe Bloggs"))
                );

答案 2 :(得分:1)

您将方法的参数作为变量传递给Expression.Block方法,而不是。如果删除它,它将正常工作:

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

当然,如果这是你的程序的范围,你也可以删除块表达式。

相关问题