Reputation: 2043
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.
Upvotes: 6
Views: 4695
Reputation: 205619
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();
}
Upvotes: 5
Reputation: 2447
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
Upvotes: 2