我曾经写过如下的扩展方法,基本上都是OrderBy
or 或者的效果ThenBy
,具体取决于源是否已经下单:
public static class Extensions {
public static IOrderedEnumerable<TSource> OrderByPreserve<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer, bool descending) {
var orderedSource = source as IOrderedEnumerable<TSource>;
if (orderedSource != null) {
return orderedSource.CreateOrderedEnumerable(keySelector, comparer, descending);
}
if (descending) {
return source.OrderByDescending(keySelector, comparer);
}
return source.OrderBy(keySelector, comparer);
}
public static IOrderedEnumerable<TSource> OrderByPreserve<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
return source.OrderByPreserve(keySelector, null, false);
}
public static IOrderedEnumerable<TSource> OrderByPreserve<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) {
return source.OrderByPreserve(keySelector, comparer, false);
}
public static IOrderedEnumerable<TSource> OrderByDescendingPreserve<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) {
return source.OrderByPreserve(keySelector, null, true);
}
public static IOrderedEnumerable<TSource> OrderByDescendingPreserve<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer) {
return source.OrderByPreserve(keySelector, comparer, true);
}
}
该接口与OrderBy
/相同(或者您可以作为布尔值OrderByDescending
传递)。descending
你可以写:
list.OrderByPreserve(x => x.A).OrderByPreserve(x => x.B)
这与以下效果相同:
list.OrderBy(x => x.A).ThenBy(x => x.B)
因此,您可以轻松地将keyboardP的解决方案与任意属性名称列表一起使用:
public static IEnumerable<TSource> OrderByProperties<TSource>(IEnumerable<TSource> source, IEnumerable<string> propertyNames) {
IEnumerable<TSource> result = source;
foreach (var propertyName in propertyNames) {
var localPropertyName = propertyName;
result = result.OrderByPreserve(x => x.GetType().GetProperty(localPropertyName).GetValue(x, null));
}
return result;
}
(localPropertyName
此处使用该变量是因为在执行查询时迭代变量会发生变化——详情请参阅此问题)
一个可能的问题是,将为每个项目执行反射操作。最好事先为每个属性构建一个 LINQ 表达式,以便可以有效地调用它们(此代码需要System.Linq.Expressions
命名空间):
public static IEnumerable<TSource> OrderByProperties<TSource>(IEnumerable<TSource> source, IEnumerable<string> propertyNames) {
IEnumerable<TSource> result = source;
var sourceType = typeof(TSource);
foreach (var propertyName in propertyNames) {
var parameterExpression = Expression.Parameter(sourceType, "x");
var propertyExpression = Expression.Property(parameterExpression, propertyName);
var castExpression = Expression.Convert(propertyExpression, typeof(object));
var lambdaExpression = Expression.Lambda<Func<TSource, object>>(castExpression, new[] { parameterExpression });
var keySelector = lambdaExpression.Compile();
result = result.OrderByPreserve(keySelector);
}
return result;
}
本质上,这些Expression
行所做的是构建表达式x => (object)x.A
(其中“A”是当前属性名称),然后将其用作排序键选择器。
示例用法是:
var propertyNames = new List<string>() { "Title", "Artist" };
var sortedList = OrderByProperties(list, propertyNames).ToList();
您只需要添加升序/降序逻辑。