Search code examples
c#reflectionlambdaexpression-trees

How to set properties and nested property's properties with expression


I googled the propblem and also search the SO. There is a ton of solutions that all of them (that I found) are not completed. Can you help me please, to set a class's properties and its nested property's properties, choosen by a lambda, using Reflection?

public class Parent
{
    public class Child
    { 
        public int Id { get; set; }
    }

    public string Name { get; private set; }
    public int Number {get; private set; }
    public Child Nested { get; set; }

    public Parent()
    {
        Nested = new Child();
    }

    public Test Set<TValue>(Expression<Func<???> func, TValue value)
    {
        // find the property name from expression
        // set the property by value
        return this;
    }
}

and in consumer, I want be able to:

Parent t = new Parent();
t.Set<int>(t => t.Number, 6)
 .set<string>(t => t.Name, "something")
 .Set<int>(t => t.Nested.Id, 25);

Solution

  • Something like this should work:

    public class Parent
    {
        public Parent Set<TValue>(Expression<Func<Parent, TValue>> func, TValue value)
        {
            MemberExpression mex = func.Body as MemberExpression;
            if(mex == null) throw new ArgumentException();
    
            var pi = mex.Member as PropertyInfo;
            if(pi == null) throw new ArgumentException();
    
            object target = GetTarget(mex.Expression);
            pi.SetValue(target, value, null);
            return this;
        }
    
        private object GetTarget(Expression expr)
        {
            switch (expr.NodeType)
            {
                case ExpressionType.Parameter:
                    return this;
                case ExpressionType.MemberAccess:
                    MemberExpression mex = (MemberExpression)expr;
                    PropertyInfo pi = mex.Member as PropertyInfo;
                    if(pi == null) throw new ArgumentException();
                    object target = GetTarget(mex.Expression);
                    return pi.GetValue(target, null);
                default:
                    throw new InvalidOperationException();
            }
        }
    }