ソースが既に注文されているかどうかに応じて、基本的にOrderBy
またはのいずれかの効果を持つ次の拡張メソッドを書いたことがあります。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();
昇順/降順のロジックを追加するだけです。