关于c#:如何从属性名称构建LINQ密钥选择器委托?

How can I build a LINQ key selector delegate from a property name?

本问题已经有最佳答案,请猛点这里访问。

通常,当提供用户指定的排序顺序,并使用LINQ进行排序时,我会遇到这样一个丑陋的场景:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static IEnumerable<ConfirmationItemViewModel> SortAscending(IEnumerable<ConfirmationItemViewModel> confirmations, string sortProperty)
{
    switch (sortProperty)
    {
        case"CreatedOn":
            confirmations = confirmations.OrderBy(i => i.CreatedOn).ToList();
            break;
        case"PaymentId":
            confirmations = confirmations.OrderBy(i => i.PaymentId).ToList();
            break;
        default:
            confirmations = confirmations.OrderBy(i => i.PaymentId).ThenBy(i => i.CreatedOn).ToList();
            break;
    }
    return confirmations;
}

OrderBy方法接受Func类型的函数委托,我假定它使用该委托从正在排序的集合中的每个项中获取sort属性的值。我想写一个方法,它使用属性名而不是委托,并返回返回属性值的委托,如果这个委托甚至解释了我的意思的一半。

希望我对它进行编码的尝试(这不起作用)能解释得更多。鉴于我对表达式和委托的理解有限,这是我所能得到的最接近的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static Func<TObject, TKey> BuildKeySelector<TObject, TKey>(TObject source, string propertyName)
{
    return obj =>
    {
        var prop = source.GetType().GetProperty(propertyName, typeof(TKey));
        return (TKey) prop.GetValue(obj);
    };
}

static void Main(string[] args)
{
    // Sort a list of Person objects by their Name property.
    var peeps = new List<Person>();
    var rank = peeps.OrderBy(BuildKeySelector(<something>,"Name"));
}


您不需要TObject object作为参数。如果您看到只使用source来获取类型,这就变得很清楚了。

你可以这样做:

1
2
3
4
5
6
7
8
public static Func<TObject, TKey> BuildKeySelector<TObject, TKey>(string propertyName)
{
    return obj =>
    {
        var prop = typeof(TObject).GetProperty(propertyName, typeof(TKey));
        return (TKey) prop.GetValue(obj);
    };
}

但是,这不是很有效,因为您的函数(从BuildKeySelector方法返回的委托)每次都会使用反射来获取属性值。更好的方法是构建表达式(可以缓存),并将表达式编译为委托。