Obtenir l'objet de MemberExpression?

c# expression-trees lambda

Question

Alors, disons que j'ai l'expression suivante en C #:

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

Comment puis-je extraire une référence à foo?

Réponse acceptée

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

Réponse populaire

J'ai eu le même problème, mais un peu plus complexe, et la réponse de Darin Dimitrov m'a donné un bon départ. Je posterai mes résultats ici, malgré le fait qu'il s'agisse d'une "vieille" question.


Cas 1: l'objet racine est un membre d'objet

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

... est équivalent à l'arbre d'expression suivant:

.                                    +====================+
.                                    |  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

Le seul endroit dans cet arbre d'expression où vous obtenez réellement une référence d'objet provient de ConstantExpression : il vous permet d'obtenir une référence à this . L'idée de base pour obtenir une référence d'objet dans cet arbre est donc la suivante:

  1. Descendez dans l'arbre d'expression le long des axes .Expression jusqu'à atteindre un nœud ConstantExpression .

  2. .Value propriété .Value ce nœud. Ceci est la référence d'objet racine (ie. this dans l'exemple ci - dessus).

  3. À l'aide de la réflexion et des nœuds MemberInfo de l'arborescence des expressions, obtenez des références aux objets et remontez dans l'arborescence des expressions.

Voici un code qui montre ceci:

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

Cas 2: l'objet racine est un membre de la classe statique

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

... donne un arbre d'expression différent. Note à la référence null en bas à gauche:

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

Ici, vous ne pouvez pas arrêter la phase de "descente" en attendant une ConstantExpression . Au lieu de cela, vous arrêtez de descendre lorsque vous atteignez une référence null. Ensuite, vous récupérez la référence de l'objet racine comme suit:

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

La phase "ascendante" à partir de là est la même que précédemment.


Il y a certainement plus de cas (tels que les paramètres nommés en tant qu'objet racine), mais j'espère qu'à présent, j'ai compris l'idée de base, alors je vais couper ici.



Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow
Sous licence: CC-BY-SA with attribution
Non affilié à Stack Overflow