How to set property value using Expressions? [duplicate]
Asked Answered
L

1

80

Given the following method:

public static void SetPropertyValue(object target, string propName, object value)
{
    var propInfo = target.GetType().GetProperty(propName,
                         BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);

    if (propInfo == null)
        throw new ArgumentOutOfRangeException("propName", "Property not found on target");
    else
        propInfo.SetValue(target, value, null);
}

How would you go about writing it's expression enabled equivalent without needing to pass in an extra parameter for target?

Why do this instead of setting the property directly I can hear you say. For example suppose we have the following class with a property that has a public getter but private setter:

public class Customer 
{
   public string Title {get; private set;}
   public string Name {get; set;}
}

I would like to be able to call:

var myCustomerInstance = new Customer();
SetPropertyValue<Customer>(cust => myCustomerInstance.Title, "Mr");

Now here is some sample code.

public static void SetPropertyValue<T>(Expression<Func<T, Object>> memberLamda , object value)
{
    MemberExpression memberSelectorExpression;
    var selectorExpression = memberLamda.Body;
    var castExpression = selectorExpression as UnaryExpression;

    if (castExpression != null)
        memberSelectorExpression = castExpression.Operand as MemberExpression;
    else
        memberSelectorExpression = memberLamda.Body as MemberExpression;

    // How do I get the value of myCustomerInstance so that I can invoke SetValue passing it in as a param? Is it possible

}

Any pointers?

Lendlease answered 7/3, 2012 at 12:39 Comment(3)
Why would you want to do that? If the property has a private setter, then it's not meant to be changed from outside the object! The function you are proposing breaks the semantics of your program.Neufer
@VladislavZorov I could see such a comment coming and I share your view. In this instance a third party DTO needs to be primed in a unit test and this would be the simplest approach of doing so. Reflection has its uses too.Lendlease
possible duplicate of How set value a property selector Expression<Func<T,TResult>> #5075984Coessential
S
160

You could cheat and make life easier with an extension method:

public static class LambdaExtensions
{
    public static void SetPropertyValue<T, TValue>(this T target, Expression<Func<T, TValue>> memberLamda, TValue value)
    {
        var memberSelectorExpression = memberLamda.Body as MemberExpression;
        if (memberSelectorExpression != null)
        {
            var property = memberSelectorExpression.Member as PropertyInfo;
            if (property != null)
            {
                property.SetValue(target, value, null);
            }
        }
    }
}

and then:

var myCustomerInstance = new Customer();
myCustomerInstance.SetPropertyValue(c => c.Title, "Mr");

The reason why this is easier is because you already have the target on which the extension method is invoked. Also the lambda expression is a simple member expression without closures. In your original example the target is captured in a closure and it could be a bit tricky to get to the underlying target and PropertyInfo.

Sumption answered 7/3, 2012 at 12:54 Comment(11)
+1 Thanks for mentioning about the closure, was wondering what the FieldExpression was doing there. This would mean that the value could be reached by the completely not elegant: ((memberSelectorExpression.Expression as MemberExpression).Expression as ConstantExpression).Value in order to get the value of the closed field. I like your approach by adding a generic extension method.Lendlease
There seems to be something wrong here. The property doesn't change, and a straight cast throws an exception: Unable to cast object of type 'System.Linq.Expressions.UnaryExpression' to type 'System.Linq.Expressions.MemberExpression'.Klarrisa
Property.SetValue is reflection. You should not use that.Olette
This doesn't seem to work for nested objects, do you know how I get around thisBlindage
You can use generic type for the value type: public static void SetPropertyValue<T, TValue>(this T target, Expression<Func<T, TValue>> memberLamda, TValue value) { var memberSelectorExpression = memberLamda.Body as MemberExpression; if (memberSelectorExpression != null) { var property = memberSelectorExpression.Member as PropertyInfo; if (property != null) { property.SetValue(target, value, null); } } }Mitten
I have incorporated @Mitten 's comment into the answer, as it worked for me.Flanigan
@Mboros: and what's wrong to use reflection? Maybe it is slow, if you make millions of operations, but occasionally...Salmi
@VikciaR: I thought the question is about expression trees, I might have misunderstood...Olette
this doesnt work for me, the property of target changes when i debug but that does not affect myCustomerInstanceDetruncate
For nested property expression, try this: codeproject.com/Articles/733296/…Tsushima
use ExpressionVisitor: public class UpdateValueVisitor : ExpressionVisitor {private readonly object newValue;private bool foundMember = false; public UpdateValueVisitor(object newValue) {this.newValue = newValue;} protected override Expression VisitMember(MemberExpression node) { if (!foundMember && node.Member is System.Reflection.PropertyInfo propertyInfo){ var newValueExpression = Expression.Constant(newValue, propertyInfo.PropertyType); var assignment = Expression.Assign(node, newValueExpression); foundMember = true; return assignment; }return base.VisitMember(node);}}Tefillin

© 2022 - 2024 — McMap. All rights reserved.