将对象从MemberExpression中取出?

所以,可以说我在C#中有以下expression式:

Expression<Func<string>> expr = () => foo.Bar; 

我如何提取foo的引用?

 Expression<Func<string>> expr = () => foo.Bar; var me = (MemberExpression)((MemberExpression)expr.Body).Expression; var ce = (ConstantExpression)me.Expression; var fieldInfo = ce.Value.GetType().GetField(me.Member.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); var value = (Foo)fieldInfo.GetValue(ce.Value); 

我也遇到了同样的问题,但是有点复杂,而达米·季米特洛夫的回答给了我一个好的开始。 尽pipe事实上这是一个“老”的问题,但我会在这里发表我的结果。


情况1:根对象是对象成员

  this.textBox.Text // where 'this' has type 'Form' 

…相当于下面的expression式树:

 . +====================+ . | MemberExpression | . +====================+ # | | # .Expression | | # vv . +------------------+ +------------+ . | MemberExpression | | MemberInfo | . +------------------+ +------------+ # | | .Name = "Text" # .Expression | | .Member .MemberType = Property # vv . +--------------------+ +------------+ . | ConstantExpression | | MemberInfo | . +--------------------+ +------------+ # .Value = this .Name = "textBox" # .Type = typeof(Form) .MemberType = Field 

在这个expression式树中,你实际得到一个对象引用的唯一地方是来自ConstantExpression :它允许你得到一个引用。 在这棵树中获得任何对象引用的基本思想是这样的:

  1. 沿着.Expression轴下降到expression式树中,直到到达ConstantExpression节点。

  2. 抓取该节点的.Value属性。 这是根对象引用(即在上例中)。

  3. 使用reflection和expression式树中的MemberInfo节点,获取对象引用,然后回到expression式树的上方。

这里有一些代码演示了这一点:

 Expression expr = ...; // <-- initially set to the expression tree's root var memberInfos = new Stack<MemberInfo>(); // "descend" toward's the root object reference: while (expr is MemberExpression) { var memberExpr = expr as MemberExpression; memberInfos.Push(memberExpr.Member); expr = memberExpr.Expression } // fetch the root object reference: var constExpr = expr as ConstantExpression; var objReference = constExpr.Value; // "ascend" back whence we came from and resolve object references along the way: while (memberInfos.Count > 0) // or some other break condition { var mi = memberInfos.Pop(); if (mi.MemberType == MemberTypes.Property) { objReference = objReference.GetType() .GetProperty(mi.Name) .GetValue(objReference, null); } else if (mi.MemberType == MemberTypes.Field) { objReference = objReference.GetType() .GetField(mi.Name) .GetValue(objReference); } } 

情况2:根对象是一个静态类成员

  Form.textBox.Text // where 'textBox' is a static member of type 'Form' 

…导致不同的expression式树。 注意左下方的空引用:

 . +====================+ . | MemberExpression | . +====================+ # | | # .Expression | | # vv . +------------------+ +------------+ . | MemberExpression | | MemberInfo | . +------------------+ +------------+ # | | .Name = "Text" # .Expression | | .Member .MemberType = Property # vv . null +------------+ . | MemberInfo | . +------------+ # .Name = "textBox" # .MemberType = Field # .DeclaringType = typeof(Form) 

在这里,你不能通过等待ConstantExpression来停止“下降”阶段。 相反,当你到达空引用时,你停止下降。 接下来,如下所示检索根对象引用:

 var mi = memberInfos.Pop(); objReference = mi.DeclaringType .GetField(member.Name, BindingFlags.Static) // or .GetProperty! .GetValue(null); 

从此之后的“上升”阶段与之前一样。


当然有更多的情况(比如命名参数作为根对象),但是我希望到现在为止,我已经有了基本的想法,所以我会在这里切断。

谢谢,staks – 你的榜样帮了我很多! 所以我想贡献一些除了第一个案件:

为了从方法中提取值,应该replace代码:

 // fetch the root object reference: var constExpr = expr as ConstantExpression; var objReference = constExpr.Value; 

与代码:

  var newExpression = expr as NewExpression; if (newExpression != null) { return newExpression.Constructor.Invoke(newExpression.Arguments.Select(GetObjectValue).ToArray()); } var methodCallExpr = expr as MethodCallExpression; if (methodCallExpr != null) { var value = methodCallExpr.Method.Invoke(methodCallExpr.Object == null ? null : GetObjectValue(methodCallExpr.Object), methodCallExpr.Arguments.Select(GetObjectValue).ToArray()); return value; } // fetch the root object reference: var constExpr = expr as ConstantExpression; if (constExpr == null) { return null; } var objReference = constExpr.Value; // ... the rest remains unchanged 

这样,人们可以从expression式中提取值:

 aInstane.MethodCall(anArgument1, anArgument2) or AType.MethodCall(anArgument1, anArgument2) or new AType().MethodCall(anArgument1, aInstane.MethodCall(anArgument2, anArgument3)) 

这是我在unit testing中使用的:

  internal static INotifyPropertyChanged SubModel<T, TProperty>(T model, Expression<Func<T, TProperty>> pickProperty) where T : INotifyPropertyChanged { MemberExpression memberExpression = (MemberExpression)pickProperty.Body; ParameterExpression parameterExpression = pickProperty.Parameters[0]; Expression mem = memberExpression.Expression; var delegateType = typeof(Func<,>).MakeGenericType(typeof(T), mem.Type); LambdaExpression lambdaExpression = Expression.Lambda(delegateType, mem, parameterExpression); object subModel = lambdaExpression.Compile().DynamicInvoke(model); return subModel as INotifyPropertyChanged ?? model; }