首页 > 解决方案 > 如何使用表达式为索引器设置属性值?

问题描述

我目前有点坚持将值设置为传递给以下函数的索引器表达式:

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

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

非常感谢你们所有人...

标签: c#reflectionexpression

解决方案


我想我看到了您正在尝试的内容 - 适用于常规和索引属性的单个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)我看不到方法的 和它所代表的属性之间有任何关系MethodInfoget因此您将不得不重新编写此代码,但这始终是反射的危险。也许寻找名称包含在 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);
    }
}

推荐阅读