简体   繁体   中英

How to set a property value from an expression tree?

I would like to set the property value referenced in an expression tree.

using System;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Reflection;

namespace ConsoleApp8
{
    class TestObject
    {
        public double X { get; set; }
    }

    class Program
    {
        static Action<double> GetSetterForX(Expression<Func<double>> expression)
        {
            var body = expression.Body;
            var operand = body as MemberExpression;
            var propertyInfo = (PropertyInfo) (operand.Member);
            var setter = propertyInfo.GetSetMethod(true);

            // At this point I have the setter. But how do I get access to the testObject?

            return null;
        }

        static void Main(string[] args)
        {
            var testObject = new TestObject();
            var setter = GetSetterForX(() => testObject.X);
            setter.Invoke(5);
            Debug.Assert(testObject.X == 5);
        }
    }
}

I can get the setter but can not find a way to get access to the instance (testObject). Is there a way?

Note that this is a simplified example. I will use much more complex expressions with many property references inside and I would like to be able to set all of them (individually).

UPDATE

To clarify. I would like to have a setter returned which only takes a double and it assigns testObject's X property. This should be possible without the need to explicitly pass in the reference to testObject in the setter. I can do the same to get a getter but not for setter. Here is the getter code:

static Func<double> GetGetterForX(Expression<Func<double>> expression)
{
    var body = expression.Body;
    var operand = body as MemberExpression;
    var result = new Func<double>(() => (double) GetValue(operand));

    return result;
}

private static object GetValue(MemberExpression member)
{
    var objectMember = Expression.Convert(member, typeof(object));
    var getterLambda = Expression.Lambda<Func<object>>(objectMember);
    var getter = getterLambda.Compile();
    return getter();
}

The getter returned is always working on the testObject instance. No need to pass in the testObject again.

As soon as the input lambda expression represents member accessor, you can use Expression.Assign passing the input lambda expression body and parameter representing the value, eg

static Action<double> GetSetterForX(Expression<Func<double>> expression)
{
    var parameter = Expression.Parameter(typeof(double), "value");
    var body = Expression.Assign(expression.Body, parameter);
    var lambda = Expression.Lambda<Action<double>>(body, parameter);
    return lambda.Compile();
}

You need to return MethodInfo instead of Action<double> , also the Invoke(object obj, object[] params) takes the original object and the parameters :

static MethodInfo GetSetterForX(Expression<Func<double>> expression)
{
    var body = expression.Body;
    var operand = body as MemberExpression;
    var propertyInfo = (PropertyInfo)(operand.Member);
    var setter = propertyInfo.GetSetMethod(true);
    return setter;
}

public static void Main()
{
    var testObject = new TestObject();
    var setter = GetSetterForX(() => testObject.X);
    setter.Invoke(testObject, new object[]{5});
    Debug.Assert(testObject.X == 5);
}

Fiddle : https://dotnetfiddle.net/CHJGbk

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM