【问题标题】: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 表示即用型代码,但您也应该展示第一次尝试。

标签: c# linq generics tree extension-methods


【解决方案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' 的异常,并且在运行时二级子级为空。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-01
  • 2021-05-22
  • 1970-01-01
  • 1970-01-01
  • 2013-05-02
  • 2014-02-08
相关资源
最近更新 更多