如何使用表达式为索引器设置属性值?

时间:2018-12-13 21:31:18

标签: c# reflection expression

目前,我有点想将值设置到传递给以下函数的索引器表达式中:

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;
        }
    }
}

我有以下课程

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

当我现在使用以下值调用先前定义的函数时,我仅获得对后备get_Item()方法的引用:

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

有人对我有提示,如何解决这个问题?任何想法都会有所帮助。

非常感谢大家...

1 个答案:

答案 0 :(得分:1)

我想我知道您在尝试什么-单个SetPropertyValue扩展名可用于常规和索引属性。在这种情况下,您需要确定传入的Expression中的引用类型,以确定如何调用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);
    }
}

您可以通过多种方法确定索引属性的名称,我决定假设以get_开头的特殊名称后跟索引属性名称(当前的C#编译器使用Item)并使用它来查找属性。如果编译器更改了名称模式(例如Item_get),则我看不到MethodInfo方法的get与它表示的属性之间的任何关系,因此您必须重新-编写此代码,但这始终是反射的危险。也许寻找一个名称包含在get方法名称中的属性会更健壮,甚至更慢。

以下是执行此操作的版本:

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