2017-04-07 146 views
1

考虑属性表达式,如t => t.MyProperty,其中t的类型为MyClass。如何在执行方法调用的新表达式中使用此属性表达式?使用表达式树中属性表达式的值

C#

class MyClass 
{ 
    public string MyProperty { get; set; } 
} 

static void Foo(string foo) 
{ 
} 

LambdaExpression GetExpression(Expression<Func<MyClass, object>> expr) 
{ 
    return expr; 
} 

var myClass = new MyClass(); 
Foo(myClass.MyProperty); 
与表达

现在...?

var expr = GetExpression(m => m.MyProperty); 
var mi = typeof(Program).GetMethod(nameof(Program.Foo), 
    BindingFlags.Public | BindingFlags.Static); 

var myClass = new MyClass(); 
// Now what?? 
// var call = Expression.Call(mi, ???expr??); 
// var invoke = Expression.Invoke(call, fooParameter); 

我想使用的expr结果和使用,在调用Foo。我知道我可以通过两个步骤来完成这项工作,我打电话给expr.Compile().DynamicInvoke(myClass)以获得价值,但这是而不是我在这里要求的。

我想构建一个表达式,它需要一个属性getter表达式,然后执行Foo(表达式)的结果调用。我无法弄清楚如何使用表达式作为方法调用的参数。

+0

目前还不清楚你试图达到什么目的。方法'Foo'采用'string';你的'Expression'产生一个'object'。你想用什么表达作为参数来调用什么方法? – dasblinkenlight

+1

如果我理解它,你应该只能使用Expression.Call(mi,Expression.Invoke(...))'? –

+0

这是挑战的一部分。也许需要转换。我很清楚自己想要达到的目标。对于给定的MyClass对象M,我想​​调用Foo(M.MyProperty)。输入是由上面的'GetExpression'方法生成的表达式。 – l33t

回答

4

根据复杂性,有两种方法可以做到这一点。在这种情况下,我们可以从重新使用这个内部表达式的参数 - 冒泡向外;我们通过丢弃旧的lambda来实现这一点,只需使用.Body.Parameters即可。例如:

var call = Expression.Lambda<Action<MyClass>>(
    Expression.Call(mi, expr.Body), expr.Parameters); 

var myClass = new MyClass { MyProperty = "yay!" }; 
call.Compile().Invoke(myClass); 

的另一种方式是在内部拉姆达使用Invoke

var outerParameter = Expression.Parameter(typeof(MyClass)); 
var typed = Expression.Convert(Expression.Invoke(expr, outerParameter), typeof(string)); 
var call = Expression.Lambda<Action<MyClass>>(Expression.Call(mi, typed), outerParameter); 

当你不能方便地控制在两块地的参数的第二种形式(Invoke)是有用的 - 或者在哪里,例如,您有多个不同参数实例的内部表达式。

+0

哇。保存了我的一天。谢谢! – l33t

+0

第二种方法对我来说实际上非常有用。谢谢。 – l33t

+0

@ l33t如果您想要,实际上还有第三个选项:使用自定义ExpressionVisitor将所有表达式替换为另一个表达式 - 例如,将内部参数替换为外部参数,但适用于任何表达式。如果您愿意,可以提供示例。 –