Create an Action<T> to "set" a property, when I am provided with the LINQ Expression for the "get"

Alex picture Alex · Jan 4, 2011 · Viewed 8.7k times · Source

I'd like to be able to generate a compiled expression to set a property, given the lambda expression that provides the "get" method for a property.

Here's what I'm looking for:

public Action<int> CreateSetter<T>(Expression<Func<T, int>> getter)
{
    // returns a compiled action using the details of the getter expression tree, or null
    // if the write property is not defined.
}

I'm still trying to understand the various types of Expression classes, so if you can point me in the right direction that would be great.

Answer

Adam Spicer picture Adam Spicer · Jan 4, 2011

Using @Ani's answer as a starting point, you can use the following to generate a compiled expression.

[TestMethod]
public void CreateSetterFromGetter()
{
    Action<Person, int> ageSetter = InitializeSet((Person p) => p.Age);
    Action<Person, string> nameSetter = InitializeSet((Person p) => p.Name);

    Person p1 = new Person();
    ageSetter(p1, 29);
    nameSetter(p1, "John");

    Assert.IsTrue(p1.Name == "John");
    Assert.IsTrue(p1.Age == 29);
}

public class Person { public int Age { get; set; } public string Name { get; set; } }

public static Action<TContainer, TProperty> InitializeSet<TContainer, TProperty>(Expression<Func<TContainer, TProperty>> getter)
{
    PropertyInfo propertyInfo = (getter.Body as MemberExpression).Member as PropertyInfo;

    ParameterExpression instance = Expression.Parameter(typeof(TContainer), "instance");
    ParameterExpression parameter = Expression.Parameter(typeof(TProperty), "param");

    return Expression.Lambda<Action<TContainer, TProperty>>(
        Expression.Call(instance, propertyInfo.GetSetMethod(), parameter),
        new ParameterExpression[] { instance, parameter }).Compile();
}

You should cache the compiled expression to keep it handy for multiple uses.