我们的想法是制作扩展方法,用于对任何树(如示例中列出的父子结构)的子项(孩子的孩子等)进行排序。 另外重要的是要说排序的属性只在运行时出现(就像列表本身一样)。
扩展方法应该与此签名类似:
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)
那么对这种扩展方法的主体有什么建议吗?
答案 0 :(得分:0)
这是一个有趣的问题。在保持性感的同时,我没有想办法如何在没有某种反射的情况下做到这一点。问题主要在于动态设置子属性。
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();