从MemberExpression中获取对象?

Bri*_*sio 38 c# lambda expression-trees

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

Expression<Func<string>> expr = () => foo.Bar;
Run Code Online (Sandbox Code Playgroud)

如何提取对foo的引用?

sta*_*ica 48

我有同样的问题,但有点复杂,Darin Dimitrov的回答给了我一个良好的开端.我会在这里发布我的结果,尽管这是一个"老"的问题.


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

    this.textBox.Text    // where 'this' has type 'Form'
Run Code Online (Sandbox Code Playgroud)

...等同于以下表达式树:

.                                    +====================+
.                                    |  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
Run Code Online (Sandbox Code Playgroud)

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

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

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

  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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

    Form.textBox.Text    // where 'textBox' is a static member of type 'Form'
Run Code Online (Sandbox Code Playgroud)

...导致不同的表达式树.请注意左下角的空引用:

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

在这里,你不能通过等待a来停止"下降"阶段ConstantExpression.而是在到达空引用时停止降序.接下来,检索根对象引用,如下所示:

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

从那时起的"提升"阶段与以前相同.


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


Dar*_*rov 40

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);
Run Code Online (Sandbox Code Playgroud)