如何从字符串为深属性创建表达式树/ lambda

And*_*ock 11 c# lambda expression-trees

给定一个字符串:"Person.Address.Postcode"我希望能够在Person的实例上获取/设置此postcode属性.我怎样才能做到这一点?我的想法是将字符串拆分为"." 然后遍历各个部分,查找前一个类型的属性,然后构建一个看起来像(伪语法道歉)的表达式树:

(person => person.Address) address => address.Postcode
Run Code Online (Sandbox Code Playgroud)

虽然我真的很麻烦地创建表达式树!如果这是最好的方式,有人可以建议如何去做,还是有更简单的选择?

谢谢

安德鲁

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; }
}
Run Code Online (Sandbox Code Playgroud)

Mar*_*ell 21

听起来你是用常规反射排序的,但是对于info,构建嵌套属性的表达式的代码与这个order-by代码非常相似.

请注意,要设置值,您需要GetSetMethod()在属性上使用并调用它 - 在构造之后没有用于分配值的内置表达式(尽管4.0中支持).

(编辑)像这样:

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();
    }
}
Run Code Online (Sandbox Code Playgroud)


Kon*_*lev 3

为什么不使用递归?就像是:

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
}
Run Code Online (Sandbox Code Playgroud)