77

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?

nawfal
  • 70,104
  • 56
  • 326
  • 368
Anastasiosyal
  • 6,494
  • 6
  • 34
  • 40
  • 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. – Vladislav Zorov Mar 07 '12 at 12:56
  • 1
    @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. – Anastasiosyal Mar 07 '12 at 13:03
  • possible duplicate of [How set value a property selector Expression>](http://stackoverflow.com/questions/8107134/how-set-value-a-property-selector-expressionfunct-tresult) http://stackoverflow.com/questions/5075484/property-selector-expressionfunct-how-to-get-set-value-to-selected-property – nawfal Apr 17 '13 at 20:43

1 Answers1

155

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.

anon
  • 4,578
  • 3
  • 35
  • 54
Darin Dimitrov
  • 1,023,142
  • 271
  • 3,287
  • 2,928
  • +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. – Anastasiosyal Mar 07 '12 at 13:12
  • 1
    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'.` – user247702 Feb 11 '13 at 07:40
  • 28
    Property.SetValue is reflection. You should not use that. – MBoros May 30 '14 at 12:26
  • 5
    This doesn't seem to work for nested objects, do you know how I get around this – Gaz Jul 08 '15 at 14:11
  • 4
    You can use generic type for the value type: `public static void SetPropertyValue(this T target, Expression> 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); } } }` – pinus.acer Oct 27 '15 at 15:43
  • 2
    I have incorporated @pinus.acer 's comment into the answer, as it worked for me. – anon May 24 '17 at 15:26
  • 4
    @Mboros: and what's wrong to use reflection? Maybe it is slow, if you make millions of operations, but occasionally... – VikciaR Sep 25 '17 at 13:10
  • 1
    @VikciaR: I thought the question is about expression trees, I might have misunderstood... – MBoros Sep 25 '17 at 22:28
  • this doesnt work for me, the property of target changes when i debug but that does not affect myCustomerInstance – roozbeh S Nov 18 '18 at 15:27
  • 1
    For nested property expression, try this: https://www.codeproject.com/Articles/733296/Expression-Parsing-and-Nested-Properties – baHI Dec 29 '18 at 09:58