Comment assigner une valeur via Expression?

c# expression-trees

Question

Ce serait très simple si je pouvais assigner via une expression Lambda (ci-dessous)

//An expression tree cannot contain an assignment operator
Expression<Func<ComplexObj, object>> expression = obj => obj.Contacts[0].FirstName = "Tim";

Ce code ci-dessus n'est pas valide en raison de l'opérateur d'affectation. Je dois passer une expression lambda afin d'identifier la propriété dans l'objet complexe à définir. Dans certains cas, l’objet complexe possède une liste et donc des types d’objets en double et des noms, c’est pourquoi j’ai besoin que le lambda fasse explicitement référence au champ de l’objet à mettre à jour.

Je suis capable de récupérer la valeur en utilisant ce qui suit, pas de problème. Mais je ne suis pas sûr de savoir comment utiliser cette même logique pour définir la valeur. J'ai découvert Expression.Assign et je pense que cela pourrait être la solution.

Expression<Func<ComplexObj, object>> expression = obj => obj.Contacts[0].FirstName;
var result = FindByProperty(expression);

public static string FindByProperty(Expression<Func<Contract, object>> propertyRefExpr)
{
    ComplexObj obj = new ComplexObj();
    Contact myContact = new Contact();
    myContact.FirstName = "Allen";
    obj.Contacts = new List<Contact>{myContact};
    return propertyRefExpr.Compile().Invoke(obj);
}

Mettre à jour:

"passer une assignation de propriété à une méthode comme un arbre d'expression ..."

À l'aide de la méthode SetValue avec ParentTypeA, Value ne fonctionnera pas. (code ci-dessous)

Expression<Func<ComplexObj, object>> expression = obj => obj.Contacts[0].FirstName;
obj.AssignNewValue(expression, firstName);

public static void AssignNewValue(this ComplexObj obj, Expression<Func<ComplexObj, object>> expression, object value)
{
    var propertyInfo = (PropertyInfo)((MemberExpression)expression.Body).Member;
    propertyInfo.SetValue(obj, value, null);
}

Réponse acceptée

J'ai fini par utiliser la solution suivante. À votre santé

ComplexObj obj = new ComplexObj();
Expression<Func<ComplexObj, object>> expression = obj => obj.Contacts[index].FirstName;
obj.AssignNewValue(expression, firstName);

public static void AssignNewValue(this ComplexObj obj, Expression<Func<ComplexObj, object>> expression, object value)
{
    ParameterExpression valueParameterExpression = Expression.Parameter(typeof(object));
    Expression targetExpression = expression.Body is UnaryExpression ? ((UnaryExpression)expression.Body).Operand : expression.Body;

    var newValue = Expression.Parameter(expression.Body.Type);
    var assign = Expression.Lambda<Action<ComplexObj, object>>
                (
                    Expression.Assign(targetExpression, Expression.Convert(valueParameterExpression, targetExpression.Type)),
                    expression.Parameters.Single(),
                    valueParameterExpression
                );

    assign.Compile().Invoke(obj, value);
}

Réponse populaire

La question liée est probablement la "bonne" réponse, mais juste pour être complet, vous pourriez faire quelque chose de plus semblable à cela ... elle suit la méthodologie "Dites, ne demandez pas" un peu mieux ne peut pas dire que j'aime la mise en œuvre ...

void Main()
{
    Expression<Func<ComplexObj, object>> expression = 
      obj => obj.Contacts[0].SetFirstName("Tim");       
}

public class ComplexObj
{
    public ComplexObj() { Contacts = new List<SimpleObj>(); }
    public List<SimpleObj> Contacts {get; private set;}
}
public class SimpleObj
{
    public string FirstName {get; private set;}
    public SimpleObj SetFirstName(string name) { this.FirstName = name; return this; }
}


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