cómo crear el árbol de expresión / lambda para una propiedad profunda de una cadena

c# expression-trees lambda

Pregunta

Dada una cadena: "Person.Address.Postcode" Quiero poder obtener / configurar esta propiedad de código postal en una instancia de Person. ¿Cómo puedo hacer esto? Mi idea era dividir la cadena por "." y luego iterar sobre las partes, buscando la propiedad en el tipo anterior, luego construir un árbol de expresiones que se parezca a algo como (disculpas por la pseudo sintaxis):

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

Sin embargo, estoy teniendo problemas reales al crear el árbol de expresiones. Si esta es la mejor manera, ¿puede alguien sugerir cómo hacerlo o hay una alternativa más fácil?

Gracias

Andrés

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

Respuesta aceptada

¿Por qué no usas la recursión? Algo como:

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
}

Respuesta experta

Parece que está ordenado con una reflexión regular, pero para información, el código para construir una expresión para propiedades anidadas sería muy similar a este código ordenado por .

Tenga en cuenta que para establecer un valor, debe usar GetSetMethod() en la propiedad e invocarlo: no hay una expresión incorporada para asignar valores después de la construcción (aunque se admite en 4.0 ).

(editar) así:

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



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué