【问题标题】:Expression to create an instance with object initializer使用对象初始化器创建实例的表达式
【发布时间】:2012-09-23 23:34:27
【问题描述】:

有没有什么方法可以使用带有表达式树的对象初始化器来创建对象的实例?我的意思是创建一个表达式树来构建这个 lambda:

// my class
public class MyObject {
    public bool DisplayValue { get; set; }
}

// my lambda:
var lambda = (Func<bool, MyObject>)
             (displayValue => new MyObject { DisplayValue = displayValue });

如何使用表达式树创建这个 lambda?

更新:

我自己试了一下,写了以下代码:

    public static Func<bool, dynamic> Creator;

    static void BuildLambda() {
        var expectedType = typeof(MyObject);
        var displayValueParam = Expression.Parameter(typeof(bool), "displayValue");
        var ctor = Expression.New(expectedType);
        var local = Expression.Parameter(expectedType, "obj");
        var displayValueProperty = Expression.Property(ctor, "DisplayValue");

        var returnTarget = Expression.Label(expectedType);
        var returnExpression = Expression.Return(returnTarget,local, expectedType);
        var returnLabel = Expression.Label(returnTarget, Expression.Default(expectedType));

        var block = Expression.Block(
            new[] { local },
            Expression.Assign(local, ctor),
            Expression.Assign(displayValueProperty, displayValueParam),
            Expression.Return(Expression.Label(expectedType), local, expectedType),
            returnExpression,
            returnLabel
            );
        Creator =
            Expression.Lambda<Func<bool, dynamic>>(block, displayValueParam)
                .Compile();
    }

但它会引发以下错误:

无法跳转到未定义的标签''。

大家可以帮帮我吗?

【问题讨论】:

标签: c# dynamic lambda expression expression-trees


【解决方案1】:

要在表达式中表示对象初始值设定项,您应该使用Expression.MemberInit()

Expression<Func<bool, MyObject>> BuildLambda() { 
    var createdType = typeof(MyObject);
    var displayValueParam = Expression.Parameter(typeof(bool), "displayValue"); 
    var ctor = Expression.New(createdType);
    var displayValueProperty = createdType.GetProperty("DisplayValue");
    var displayValueAssignment = Expression.Bind(
        displayValueProperty, displayValueParam);
    var memberInit = Expression.MemberInit(ctor, displayValueAssignment);

    return
        Expression.Lambda<Func<bool, MyObject>>(memberInit, displayValueParam);
}

要验证这是否确实符合您的要求,您可以在创建的表达式上调用 ToString()。在这种情况下,输出如预期:

displayValue => new MyObject() {DisplayValue = displayValue}

【讨论】:

【解决方案2】:

终于找到答案了:

public static Func<bool, dynamic> Creator; 

static void BuildLambda() { 
    var expectedType = typeof(MyObject); 
    var displayValueParam = Expression.Parameter(typeof(bool), "displayValue"); 
    var ctor = Expression.New(expectedType); 
    var local = Expression.Parameter(expectedType, "obj"); 
    var displayValueProperty = Expression.Property(local, "DisplayValue"); 

    var returnTarget = Expression.Label(expectedType); 
    var returnExpression = Expression.Return(returnTarget,local, expectedType); 
    var returnLabel = Expression.Label(returnTarget, Expression.Default(expectedType)); 

    var block = Expression.Block( 
        new[] { local }, 
        Expression.Assign(local, ctor), 
        Expression.Assign(displayValueProperty, displayValueParam), 
        /* I forgot to remove this line:
         * Expression.Return(Expression.Label(expectedType), local, expectedType), 
         * and now it works.
         * */
        returnExpression, 
        returnLabel 
        ); 
    Creator = 
        Expression.Lambda<Func<bool, dynamic>>(block, displayValueParam) 
            .Compile(); 
}

更新:

虽然它工作正常,但@svick 在他的答案中提供了一种更好更短的方法,这正是我一直在寻找的:MemberInit。请参阅@svick 的回答。

【讨论】:

  • 如果您只想编译和执行代码,这会很好。但是,如果您想以其他方式使用表达式(例如在 LINQ to SQL 中),这可能效果不佳。如我的回答所示,应该使用MemberInit()。这样,您还将获得更短、更易读的代码。
  • @svick 非常感谢。我想做编译和缓存函数来使用。
猜你喜欢
  • 2013-08-22
  • 2013-06-27
  • 2012-10-02
  • 1970-01-01
  • 2013-07-04
  • 1970-01-01
  • 2016-10-26
  • 1970-01-01
  • 2018-08-14
相关资源
最近更新 更多