65.9K
CodeProject 正在变化。 阅读更多。
Home

运行时基于 LINQ 的通用多级对象排序器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.43/5 (4投票s)

2009 年 10 月 26 日

CPOL

2分钟阅读

viewsIcon

16339

一个通用辅助函数,可动态地按编译时未知的对象属性对对象进行排序。

引言

我最近遇到了一种情况,我有一个对象列表,每个对象都有属性。这些对象是从 Active Directory 提取的,AD 对象的各种属性加载在 StringDictionary 中。我希望能够根据配置对这些对象进行排序,而无需硬编码任何特定的属性或排序顺序,并且我希望能够按多个属性排序,例如,LastName 升序,然后 FirstName 降序。

当然,你不能简单地遍历要排序的属性列表并进行排序,因为每次排序都会清除之前的排序结果。

LINQ 提供了处理这种情况的方法,OrderByThenBy。但如果不知道编译时要排序的属性,这就会稍微复杂一些。在本文中,我演示了一个简单的通用函数,它可以帮助对数据列表进行任意数量的排序键排序,并且每个排序级别都支持升序和降序。

函数

这是辅助函数

private IEnumerable<T> MultiLevelSort<T, SK>(
    IEnumerable<T> list, 
    List<SK> sortKeys, 
    Func<T, SK, string> keySelector, 
    Func<SK, bool> ascendingSelector)
{
    if (sortKeys.Count == 0) return list;
    IOrderedEnumerable<T> res = null;
    for (int i = 0; i < sortKeys.Count; i++)
    {
        SK sk = sortKeys[i];
        bool ascending = ascendingSelector(sk);
        if (i == 0)
        {
            if (ascending) res = list.OrderBy(r => keySelector(r, sk));
            else res = list.OrderByDescending(r => keySelector(r, sk));
        }
        else 
        {
            if (ascending) res = res.ThenBy(r => keySelector(r, sk));
            else res = res.ThenByDescending(r => keySelector(r, sk));
        }
    }
    return res;
}  

这段代码接受四个参数

  1. 一个 IEnumerable<T> 对象列表,用于排序
  2. 一个 List<SK> 对象列表,其中包含排序顺序信息(请注意,此列表本身预计已按正确的排序顺序排列)
  3. 一个 Func<T, SK, string>,用于根据 SK 中的信息从 T 中提取用于实际排序的值
  4. 一个 Func<SK, bool>,用于从 SK 中提取升序/降序信息 - true 表示升序,false 表示降序

…它返回正确排序的列表作为 IEnumerable<T>。请注意,只要至少有一个有效的排序键,实际返回的对象将是 IOrderedEnumerable<T>

Using the Code

这是一个如何使用辅助函数的示例。它假定你有一个名为“AllProperties”的对象中包含“MyProperty”的列表,其中一些指定了排序信息。

List<MyProperty> sortProps = AllProperties
                                .Where(sp => sp.Sort != string.Empty)
                                .OrderBy(sp => sp.SortOrder).ToList();
IEnumerable<MyObject> sortedResults = MultiLevelSort<MyObject, MyProperty>(
    results, sortProps,
    (r, sp) => r.Properties.ContainsKey(sp.Name) ? r.Properties[sp.Name] : string.Empty,
    sp => sp.Sort == "Ascending"
        );  

... 在此示例中,MyObject 是一个包含名为“Properties”的 StringDictionary 的对象,而 MyProperty 是一个包含名为“Sort”(“升序/降序”)、“SortOrder”(一个整数)和“Name”(我们想要在 MyObject.Properties 集合中排序的属性的名称)的属性的对象。

你的对象显然可以拥有自己的结构或属性来提供用于排序的数据 - 它不必像此示例中的 StringDictionary 一样。

历史

  • v1.0 - 2009 年 10 月 26 日 - 首次发布
© . All rights reserved.