代码之家  ›  专栏  ›  技术社区  ›  Brian Genisio

从MemberExpression中获取对象?

  •  37
  • Brian Genisio  · 技术社区  · 15 年前

    所以,假设我在c_中有以下表达式:

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

    我怎样才能找到foo的参考?

    5 回复  |  直到 7 年前
        1
  •  38
  •   Darin Dimitrov    15 年前
    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);
    
        2
  •  47
  •   stakx - no longer contributing Saravana Kumar    7 年前

    我也有同样的问题,但有点复杂,达林·迪米特洛夫的回答给了我一个良好的开端。尽管这是一个“老”问题,我还是会在这里发布我的结果。


    案例1:根对象是对象成员

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

    …相当于以下表达式树:

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

    在这个表达式树中,您实际获得对象引用的唯一位置是 ConstantExpression :它允许您获取 this . 因此,在此树中获取任何对象引用的基本思想如下:

    1. 沿着 .Expression 直到你到达 恒压 节点。

    2. 抓住那个节点 .Value 财产。这是根对象引用(即。 在上面的例子中)。

    3. 使用反射和 MemberInfo 从表达式树中获取节点,获取对象引用并以“向上”的方式返回表达式树。

    下面的代码演示了这一点:

    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'
    

    …结果生成不同的表达式树。请注意左下角的空引用:

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

    在这里,你不能通过等待一个 恒压 . 相反,当到达空引用时停止降序。接下来,检索根对象引用,如下所示:

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

    从那里开始的“提升”阶段和以前一样。


    当然还有更多的情况(例如命名参数作为根对象),但是我希望到现在为止,我已经了解了基本的概念,所以我将在这里中断。

        3
  •  1
  •   4b0 Agit    7 年前

    有一个更简单的解决方案:

    var pExpression = ((MemberExpression)expr.Body);
    var bindingObject = Expression.Lambda(((MemberExpression)pExpression.Expression)).Compile().DynamicInvoke();
    
        4
  •  0
  •   Andrey Ilnitsky    12 年前

    谢谢,斯塔克-你的榜样帮了我很多忙!因此,我想在第一个案例的基础上增加一些内容:

    要从方法中提取值,应替换代码:

    // 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
    

    这样就可以从如下表达式中提取值:

    aInstane.MethodCall(anArgument1, anArgument2) or
    AType.MethodCall(anArgument1, anArgument2) or
    new AType().MethodCall(anArgument1, aInstane.MethodCall(anArgument2, anArgument3))
    
        5
  •  0
  •   4b0 Agit    7 年前

    这就是我在单元测试中使用的:

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