给定一个类型为ExpressionType.MemberAccess的表达式,如何获取字段值?

36

我正在解析一个表达式树。如果给定一个ExpressionType.MemberAccess节点类型,我如何获取该字段的值?

根据C# MSDN文档: MemberAccess是表示从字段或属性中读取的节点。

一个代码片段将非常、非常有帮助。提前感谢!!!

我的代码大致如下:

public static List<T> Filter(Expression<Func<T, bool>> filterExp) 
{
//the expression is indeed a binary expression in this case
BinaryExpression expBody = filterExp.Body as BinaryExpression;

if (expBody.Left.NodeType == ExpressionType.MemberAccess) 
  //do something with ((MemberExpressionexpBody.Left).Name

//right hand side is indeed member access. in fact, the value comes from //aspdroplist.selectedvalue            
if (expBody.Right.NodeType == ExpressionType.MemberAccess)
{
   //how do i get the value of aspdroplist.selected value?? note: it's non-static                        
}

//return a list
}
2个回答

46

[为了更清晰明了,已更新]

首先,将Expression强制转换为MemberExpression

MemberExpression有两个有趣的属性:

  • .Member - 成员的PropertyInfo / FieldInfo
  • .Expression - 用于评估以获取 Member 的“obj”的表达式

例如,如果你可以将.Expression评估为“obj”,并且.Member是一个FieldInfo,那么你就可以通过在FieldInfo上调用.GetValue(obj)来获取实际值(而PropertyInfo非常相似)。

问题在于评估.Expression非常棘手。;-p

显然,如果它恰好是ConstantExpression,你会很幸运 - 但在大多数情况下,它不是;它可能是一个ParameterExpression(在这种情况下,你需要知道要评估的实际参数值),或任何其他组合的Expression

在许多情况下,一个简单的(也许是懒惰的)选择是使用.Compile()让.NET框架来完成繁重的工作;然后,你可以将lambda评估为一个类型化的委托(传递任何lambda所需的参数)。然而,并不总是可行。

为了说明这有多么复杂,请考虑这个简单的例子(在其中我已经硬编码了每一步,而没有进行测试等):

using System;
using System.Linq.Expressions;
using System.Reflection;
class Foo
{
    public string Bar { get; set; }
}

static class Program
{
    static void Main()
    {
        Foo foo = new Foo {Bar = "abc"};
        Expression<Func<string>> func = () => foo.Bar;

        MemberExpression outerMember = (MemberExpression)func.Body;
        PropertyInfo outerProp = (PropertyInfo) outerMember.Member;
        MemberExpression innerMember = (MemberExpression)outerMember.Expression;
        FieldInfo innerField = (FieldInfo)innerMember.Member;
        ConstantExpression ce = (ConstantExpression) innerMember.Expression;
        object innerObj = ce.Value;
        object outerObj = innerField.GetValue(innerObj);
        string value = (string) outerProp.GetValue(outerObj, null);    
    }

}

非常感谢你,Marc。.Expression 属性的值是......更有趣的一些东西:{value(ASP.usercontrols_mycontro_ascx)。controlname} - Keith Fitzgerald
我正在大量使用泛型和反射,因此通过PropertyInfo/FieldInfo检索值不起作用,因为我不确定从哪里提取引用对象...我可以从MemberExpression或MethodInfo中提取吗? - Keith Fitzgerald
1
它可以正常工作...但问题是您需要将.Expression评估为一个值,以将其作为FieldInfo/PropertyInfo的“obj”提供。您不能只使用.Compile()并将lambda作为委托执行吗?这比解析要简单得多... - Marc Gravell
请注意,如果Lambda表达式是带参数的,但您没有特定的参数,则会遇到困难...但像ConstantExpression这样的东西应该可以自己解决。 - Marc Gravell
非常感谢你,马克!!!你真的做得太好了。最终问题是左侧是二进制表达式...而右侧不是常量表达式。我将更新我的问题并附上一些代码。 - Keith Fitzgerald

25

非常感谢上面的Marc Gravell。我非常感激他的帮助。

事实证明,在我的情况下,问题可以通过以下方式解决:

object value = Expression.Lambda(expBody.Right).Compile().DynamicInvoke();

再次感谢你,马克!


5
object value = Expression.Lambda<Func<object>>(Expression.Convert(expBody.Right, typeof(object))).Compile().Invoke() 将表达式树的右侧转换为 object 类型,然后编译并调用 Lambda 表达式,最终返回一个 object 值。 - Double Down
遗憾的是:它非常缓慢。 - ren

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接