Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 775
RM. Avatar asked Sep 11 '19 08:09

RM.


People also ask

How does an expression tree work?

Expression trees represent code in a tree-like data structure, where each node is an expression, for example, a method call or a binary operation such as x < y . You can compile and run code represented by expression trees.

What are the advantages of expression tree?

Expression trees allow you to build code dynamically at runtime instead of statically typing it in the IDE and using a compiler. They are well explained in the documentation.

What is expression trees and how they used in LINQ?

Expression Trees was first introduced in C# 3.0 (Visual Studio 2008), where they were mainly used by LINQ providers. Expression trees represent code in a tree-like format, where each node is an expression (for example, a method call or a binary operation such as x < y).

What is query expression trees?

An expression tree is a representation of expressions arranged in a tree-like data structure. In other words, it is a tree with leaves as operands of the expression and nodes contain the operators. Similar to other data structures, data interaction is also possible in an expression tree.


2 Answers

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, e.g.

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();
}
like image 87
Ivan Stoev Avatar answered Sep 23 '22 13:09

Ivan Stoev


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

like image 45
Fourat Avatar answered Sep 21 '22 13:09

Fourat