在树状通用列表中按字母数字顺序对子项进行排序的扩展方法

Posted

技术标签:

【中文标题】在树状通用列表中按字母数字顺序对子项进行排序的扩展方法【英文标题】:Extension method to Order children alphanumerically in a tree-like generic list 【发布时间】:2013-12-31 00:35:45 【问题描述】:

这个想法是为任何树状列表(具有示例中列出的父子结构)的子项(子项的子项等)进行排序的扩展方法。 还有一点很重要,要排序的属性只有在运行时才知道(列表本身也是如此)。

扩展方法应该有类似这个签名的东西:

public static IEnumerable<T> OrderChildren<T>(
this IEnumerable<T> source,
Func<T, IEnumerable<T>> childrenSelector,
Func<T, string> orderSelector)

...

这是创建列表的示例类:

public class Example

    public IEnumerable<Example> Children;

    public string Name;

    public int Id;

    public Example Parent;

还有一些模拟数据:


   ...
   var collection = MockUp();
   ...


private static IEnumerable<Example> MockUp()

    var rootCollection = new List<Example>();
    var firstLevelCollectionA = new List<Example>();
    var firstLevelCollectionB = new List<Example>();
    var secondLevelCollectionAA = new List<Example>();
    var secondLevelCollectionAB = new List<Example>();
    var secondLevelCollectionBA = new List<Example>();
    var secondLevelCollectionBB = new List<Example>();

    secondLevelCollectionAA.Add(new Example() Name = "SecondLvlAA1");
    secondLevelCollectionAA.Add(new Example() Name = "SecondLvlAA2");

    secondLevelCollectionAB.Add(new Example() Name = "SecondLvlAB1");
    secondLevelCollectionAB.Add(new Example() Name = "SecondLvlAB2");

    secondLevelCollectionBA.Add(new Example() Name = "SecondLvlBA1");
    secondLevelCollectionBA.Add(new Example() Name = "SecondLvlBA2");

    secondLevelCollectionBB.Add(new Example() Name = "SecondLvlBB1");
    secondLevelCollectionBB.Add(new Example() Name = "SecondLvlBB2");

    firstLevelCollectionA.Add(new Example() Name = "FirstLvlA1", Children = secondLevelCollectionAA);
    firstLevelCollectionA.Add(new Example() Name = "FirstLvlA2", Children = secondLevelCollectionAB);

    firstLevelCollectionB.Add(new Example() Name = "FirstLvlB1", Children = secondLevelCollectionBA);
    firstLevelCollectionB.Add(new Example() Name = "FirstLvlB2", Children = secondLevelCollectionBB);

    rootCollection.Add(new Example() Name = "Root1", Children = firstLevelCollectionA);
    rootCollection.Add(new Example() Name = "Root2", Children = firstLevelCollectionB);

    return rootCollection;

然后我们需要调用扩展方法OrderChildren,像这样:

collection.OrderChildren(x => x.Children, x => x.Name)

那么对这个扩展方法的主体有什么建议吗?

【问题讨论】:

+1 表示即用型代码,但您也应该展示第一次尝试。 【参考方案1】:

这是一个有趣的问题。在保持性感的同时,我不知道如何在没有某种反思的情况下做到这一点。问题主要在于动态设置 children 属性。

public static IEnumerable<T> OrderRecursively<T, TKey>(
    this IEnumerable<T> source,
    Expression<Func<T, IEnumerable<T>>>  childrenExpression,
    Func<T, TKey> keySelector)

    if (source == null)
        throw new Exception("source");

    var memberExpression = childrenExpression.Body as MemberExpression;
    if (memberExpression == null)
        throw new Exception("memberExpression");

    var propertyInfo = memberExpression.Member as PropertyInfo; 
    if(propertyInfo == null)
        throw new Exception("propertyInfo");

    var instance = Expression.Parameter(typeof (T), "instance");
    var parameter = Expression.Parameter(typeof (IEnumerable<T>), "param");

    var childrenSet = Expression.Lambda<Action<T, IEnumerable<T>>>(
        Expression.Call(instance, propertyInfo.GetSetMethod(), parameter),
        new[] instance, parameter).Compile();

    return OrderRecursivelyReflectionDarkness(
        source, 
        childrenExpression.Compile(), 
        keySelector, 
        childrenSet);


private static IEnumerable<T> OrderRecursivelyReflectionDarkness<T, TKey>(
    this IEnumerable<T> source,
    Func<T, IEnumerable<T>>  childrenSelector,
    Func<T, TKey> keySelector,
    Action<T, IEnumerable<T>> childrenSetter)

    foreach (var parent in source.OrderBy(keySelector))
    
        var children = childrenSelector(parent);
        if (children != null && children.Any())
        
            var sortedChildren = children.OrderRecursivelyReflectionDarkness(
                childrenSelector,
                keySelector,
                childrenSetter);

            childrenSetter(parent, sortedChildren);
            yield return parent;
        
    

您需要注意 memberExpression.Member,因为它也可以是字段。

也可以避免反射,但是用起来会有点尴尬。

public static IEnumerable<T> OrderRecursively<T, TKey>(
    this IEnumerable<T> source,
    Func<T, IEnumerable<T>>  childrenSelector,
    Func<T, TKey> keySelector,
    Action<T, IEnumerable<T>> childrenSet)

    if (source == null)
        throw new Exception("source");

    return OrderRecursivelyReflectionDarkness(
        source, 
        childrenSelector, 
        keySelector, 
        childrenSet);


//and usage
collection = collection.OrderRecursively(
x => x.Children, 
x => x.Name, 
(parent, sortedChildren) => parent.Children = sortedChildren).ToList();

【讨论】:

Tnx 用于响应。我似乎无法通过FirstLvl。 (无论有没有反射,我都会得到 'new System.Linq.SystemCore_EnumerableDebugView(new System.Collections.Generic.Mscorlib_CollectionDebugView(collection as System.Collections.Generic.List).Items[0].Children).Items ' 引发了类型为 'System.Linq.SystemCore_EnumerableDebugViewEmptyException' 的异常,并且在运行时二级子级为空。

以上是关于在树状通用列表中按字母数字顺序对子项进行排序的扩展方法的主要内容,如果未能解决你的问题,请参考以下文章

在mysql中按字母顺序对全名进行排序

在Android中按字母顺序排序文件列表,文件夹然后文件

在自定义 foreach 循环中按字母顺序对结果进行排序

在C ++中按非ASCII顺序的第一个字母对字符串向量进行排序

如何在 jquery 选择的下拉列表中按字母顺序重新排序 <options>

我如何测试使用硒在下拉菜单中按字母顺序对项目进行排序