comment créer expression arbre / lambda pour une propriété deep à partir d'une chaîne

c# expression-trees lambda

Question

Avec une chaîne: "Person.Address.Postcode", je veux pouvoir obtenir / définir cette propriété de code postal sur une instance de Person. Comment puis-je faire ceci? Mon idée était de scinder la chaîne par "." puis parcourez les parties, recherchez la propriété du type précédent, puis créez un arbre d’expression ressemblant à quelque chose comme (excuses pour la pseudo-syntaxe):

(person => person.Address) address => address.Postcode

Cependant, j'ai vraiment du mal à créer l'arbre d'expression! Si tel est le meilleur moyen, quelqu'un peut-il suggérer comment s'y prendre, ou existe-t-il une alternative plus facile?

Merci

Andrew

public class Person
{
    public int Age { get; set; }
    public string Name { get; set; }
    public Address Address{ get; set; }

    public Person()
    {
        Address = new Address();
    }
}

public class Address 
{
    public string Postcode { get; set; }
}

Réponse acceptée

Pourquoi n'utilisez-vous pas la récursivité? Quelque chose comme:

setProperyValue(obj, propertyName, value)
{
  head, tail = propertyName.SplitByDotToHeadAndTail(); // Person.Address.Postcode => {head=Person, tail=Address.Postcode}
  if(tail.Length == 0)
    setPropertyValueUsingReflection(obj, head, value);
  else
    setPropertyValue(getPropertyValueUsingReflection(obj, head), tail, value); // recursion
}

Réponse d'expert

On dirait que vous avez été trié avec une réflexion régulière, mais pour info, le code permettant de créer une expression pour les propriétés imbriquées serait très similaire à ce code trié par ordre .

Notez que pour définir une valeur, vous devez utiliser GetSetMethod() sur la propriété et l'invoquer. Aucune expression intégrée ne permet d'affecter des valeurs après la construction (bien qu'elle soit prise en charge en 4.0 ).

(éditer) comme ceci:

using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
class Foo
{
    public Foo() { Bar = new Bar(); }
    public Bar Bar { get; private set; }
}
class Bar
{
    public string Name {get;set;}
}
static class Program
{
    static void Main()
    {
        Foo foo = new Foo();
        var setValue = BuildSet<Foo, string>("Bar.Name");
        var getValue = BuildGet<Foo, string>("Bar.Name");
        setValue(foo, "abc");
        Console.WriteLine(getValue(foo));        
    }
    static Action<T, TValue> BuildSet<T, TValue>(string property)
    {
        string[] props = property.Split('.');
        Type type = typeof(T);
        ParameterExpression arg = Expression.Parameter(type, "x");
        ParameterExpression valArg = Expression.Parameter(typeof(TValue), "val");
        Expression expr = arg;
        foreach (string prop in props.Take(props.Length - 1))
        {
            // use reflection (not ComponentModel) to mirror LINQ 
            PropertyInfo pi = type.GetProperty(prop);
            expr = Expression.Property(expr, pi);
            type = pi.PropertyType;
        }
        // final property set...
        PropertyInfo finalProp = type.GetProperty(props.Last());
        MethodInfo setter = finalProp.GetSetMethod();
        expr = Expression.Call(expr, setter, valArg);
        return Expression.Lambda<Action<T, TValue>>(expr, arg, valArg).Compile();        

    }
    static Func<T,TValue> BuildGet<T, TValue>(string property)
    {
        string[] props = property.Split('.');
        Type type = typeof(T);
        ParameterExpression arg = Expression.Parameter(type, "x");
        Expression expr = arg;
        foreach (string prop in props)
        {
            // use reflection (not ComponentModel) to mirror LINQ 
            PropertyInfo pi = type.GetProperty(prop);
            expr = Expression.Property(expr, pi);
            type = pi.PropertyType;
        }
        return Expression.Lambda<Func<T, TValue>>(expr, arg).Compile();
    }
}


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