5

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

......当然不是。顾名思义,这是一段代码,呵呵!

4

3 回答 3

8

澄清一下,BlockExpression 表示按顺序执行的一系列表达式,并返回最后一个表达式的值。

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

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

所以它显然会抛出 NullReferenceException。

于 2013-11-08T14:22:31.993 回答
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"))
                );
于 2013-11-08T14:15:16.107 回答
1

您将方法的参数作为变量传递给 Expression.Block 方法,但事实并非如此。如果删除它,它将正常工作:

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

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

于 2013-11-08T14:21:09.440 回答