Donc, disons que j'ai l'expression suivante en C#:
Expression<Func<string>> expr = () => foo.Bar;
Comment puis-je sortir d'une référence à toto?
Donc, disons que j'ai l'expression suivante en C#:
Expression<Func<string>> expr = () => foo.Bar;
Comment puis-je sortir d'une référence à toto?
J'ai eu le même problème, mais un peu plus complexe, et Darin Dimitrov réponse m'a donné un bon début. Je vais poster mes résultats ici, malgré le fait que c'est un "vieux" de la question .
Cas 1: L'objet racine est un membre de l'objet
this.textBox.Text // where 'this' has type 'Form'
... est équivalent à l'expression suivante de l'arbre:
. +====================+ . | MemberExpression | . +====================+ # | | # .Expression | | # 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 cette expression de l'arbre où vous obtenez en fait un objet de référence est de l'
ConstantExpression
: il vous permet d'obtenir une référence à l'this
. L'idée de base pour obtenir un objet de référence dans cette arborescence est donc comme suit:
Descendre dans l'arborescence d'expression le long de la
.Expression
axes jusqu'à ce que vous atteignez unConstantExpression
nœud.Prenez du nœud
.Value
de la propriété. C'est la racine de la référence d'objet (ie.this
dans l'exemple ci-dessus).L'utilisation de la réflexion et de l'
MemberInfo
des nœuds de l'arborescence d'expression, d'obtenir les références de l'objet et de travailler votre chemin de retour vers le "haut" de l'arborescence d'expression.Voici un code qui illustre cela:
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 classe statique
Form.textBox.Text // where 'textBox' is a static member of type 'Form'
... les résultats dans une expression différente de l'arbre. Note de la référence nulle en bas à gauche:
. +====================+ . | MemberExpression | . +====================+ # | | # .Expression | | # 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 de le "descendre" phase d'attente pour un
ConstantExpression
. Au lieu de cela, vous vous arrêtez décroissant lorsque vous atteignez une référence null. Ensuite, vous pouvez récupérer la racine de la référence de l'objet comme suit:var mi = memberInfos.Pop(); objReference = mi.DeclaringType .GetField(member.Name, BindingFlags.Static) // or .GetProperty! .GetValue(null);
Le "monter" phase à partir de là, partir est la même qu'avant.
Il y a certainement plus de cas (tels que les paramètres nommés en tant que racine de l'objet), mais j'espère que, maintenant, j'ai l'idée de base de partout, donc je vais couper ici.
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);
Merci, staks - votre exemple m'a beaucoup aidé! Donc j'aimerais contribuer, certains plus pour le premier cas:
Pour extraire des valeurs de méthodes, il faut remplacer le code:
// fetch the root object reference:
var constExpr = expr as ConstantExpression;
var objReference = constExpr.Value;
avec le code:
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
de cette manière, on pourrait extraire des valeurs d'expressions comme:
aInstane.MethodCall(anArgument1, anArgument2) or
AType.MethodCall(anArgument1, anArgument2) or
new AType().MethodCall(anArgument1, aInstane.MethodCall(anArgument2, anArgument3))
C'est ce que j'utilise pour les tests unitaires:
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;
}
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.