0

I am currently kind of stuck with setting a value into an indexer expression passed into the following function:

private 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);
            return;
        }
    }
}

I have the following class

class Entity 
{
    public object this[string name] 
    {
        get { /* */ }
        set { /* */ }
    }
}

When I now call the previous defined function with the following values I only get a reference to the backing get_Item() method:

var entity = new Entity();
// ...
SetPropertyValue(entity, x => x[memberName], value);

Does anybody have a hint for me, how to solve this issue? Any idea will help.

Many thanks to all of you...

MacX
  • 587
  • 5
  • 16
  • I think this *might* (not sure) be related to one of my old questions: [Create dynamic method to assign value to a property](https://stackoverflow.com/questions/4588175/create-dynamicmethod-to-assign-value-to-a-property) –  Dec 13 '18 at 21:37
  • Do you expect `x[membername] = value` to be valid? What is `membername`? Think about what `x[membername]` means... what do the type arguments of `Func<>` represent? – NetMage Dec 13 '18 at 23:12

1 Answers1

1

I think I see what you are attempting - a single SetPropertyValue extension that works for regular and indexed properties. In that case you need to determine the type of reference in the passed in Expression to determine how to call SetValue:

public static void SetPropertyValue<T, TValue>(this T target, Expression<Func<T,TValue>> memberFn, TValue value) {
    var b = memberFn.Body;
    if (b is MethodCallExpression bc && bc.Method.IsSpecialName && bc.Method.Name.StartsWith("get_")) {
        var PI = typeof(T).GetProperty(bc.Method.Name.Substring(4));
        PI.SetValue(target, value, bc.Arguments.Select(a => a.Evaluate<object>()).ToArray());
    }
    else if (b is MemberExpression bm) {
        var pi = bm.Member;
        pi.SetValue(target, value);
    }
}

You can determine the indexed property name a number of different ways, I decided to assume a special name beginning with get_ would be followed by the indexed property name (the current C# compilers use Item) and use that to find the property. If the compiler changed the name pattern, (e.g. Item_get) I don't see any relationship between the MethodInfo of the get method and the property it represents, so you would have to re-write this code, but that is always a hazard of Reflection anyway. Perhaps looking for a property whose name is contained by the get method name would be slightly more robust, if slower.

Here is a version that does that:

public static void SetPropertyValue<T, TValue>(this T target, Expression<Func<T,TValue>> memberFn, TValue value) {
    var b = memberFn.Body;
    if (b is MethodCallExpression bc && bc.Method.IsSpecialName) {
        var PI = typeof(T).GetProperties().First(pi => bc.Method.Name.Contains(pi.Name));
        PI.SetValue(target, value, bc.Arguments.Select(a => a.Evaluate<object>()).ToArray());
    }
    else if (b is MemberExpression bm) {
        var pi = bm.Member;
        pi.SetValue(target, value);
    }
}
NetMage
  • 26,163
  • 3
  • 34
  • 55
  • That is exactly the thing I want to achieve. I will try it and give feedback to you.# – MacX Dec 14 '18 at 06:57
  • Wow... thanks. It works pretty well. But I recognized, that this reflection logic is not really necessary because I had all the options in my hand to explicitly use a setter action, depending on the input. Therefor I also achieved a performance improvement not using reflection. – MacX Dec 14 '18 at 07:09
  • @MacX Excellent on avoiding reflection - always best to use non-reflection tools. I checked and it seems the C# specification specifies the name of the methods and property for an [Indexer](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes#class-members) so just using `Item` directly is probably safe. – NetMage Dec 14 '18 at 18:58