SQL チックなメソッド名
C# の LINQ to Object(以下、単に LINQ と記載) はとても便利なんですけど、メソッド名が SQL っぽくて、いまいち直感的ではない上にダサいです。例として、JavaScript と比較してみましょう。
LINQ でのメソッド名 | JavaScript のメソッド名 |
---|---|
Select | map |
Where | filter |
OrderBy | sort |
以下は、実際に利用する例です。
JavaScript ではこう。
const hoge = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0];
const fuga = hoge.filter(x => x % 2 === 0).map(x => x * 2).sort();
C# ではこう。
var hoge = new[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
var fuga = hoge.Where(x => x % 2 == 0).Select(x => x * 2).OrderBy(x => x).ToArray();
もちろん慣れてくれば Select
だろうが Where
だろうがスラスラ読めるのですが、慣れていなければ引っかかるかもしれません。一方、map
, filter
, sort
というのは直感的で意味がわかりやすいです。
ラップする
ということで、ラッパーメソッドで名前を変えます。
Select
=> Map
public static IEnumerable<TResult> Map<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> func)
=> source.Select(x => func(x));
public static IEnumerable<TResult> Map<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, int, TResult> func)
=> source.Select((x, index) => func(x, index));
Where
=> Filter
public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, Func<bool> func)
=> source.Where(x => func());
public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, Func<T, bool> func)
=> source.Where(x => func(x));
public static IEnumerable<T> Filter<T>(this IEnumerable<T> source, bool func)
=> source.Where(x => func);
OrderBy
=> Sort
public static IOrderedEnumerable<TSource> Sort<TSource, TKey>(this IEnumerable<TSource> sources, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
=> sources.OrderBy(keySelector, comparer);
public static IOrderedEnumerable<TSource> Sort<TSource, TKey>(this IEnumerable<TSource> sources, Func<TSource, TKey> keySelector)
=> sources.OrderBy(keySelector);
public static IOrderedEnumerable<TSource> SortByDesc<TSource, TKey>(this IEnumerable<TSource> sources, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
=> sources.OrderByDescending(keySelector, comparer);
public static IOrderedEnumerable<TSource> SortByDesc<TSource, TKey>(this IEnumerable<TSource> sources, Func<TSource, TKey> keySelector)
=> sources.OrderByDescending(keySelector);
ソートに関しては、元の OrderBy
が、昇順か降順かでメソッドが別れています。昇順のソートが OrderBy
、降順のソートが OrderByDescending
なので、それぞれ Sort
、 SortByDesc
というメソッド名でラップしましたが、ここは昇順か降順かをオプション引数で切り替える Sort
という名前のメソッドにしても良いでしょう。
「昇順か降順かをメソッド名だけで判断できる」という利点はありますが、同じようなことが別の名前のメソッドで定義されているのも、個人的には気持ち悪いと感じます。
同様に、ThenBy
と ThenByDescending
も Then
というメソッドでラップしてみます。
public static IOrderedEnumerable<TSource> Sort<TSource, TKey>(this IEnumerable<TSource> sources, Func<TSource, TKey> keySelector, IComparer<TKey> comparer, bool byDesc)
=> byDesc ? sources.OrderByDescending(keySelector, comparer)
: sources.OrderBy(keySelector, comparer);
public static IOrderedEnumerable<TSource> Sort<TSource, TKey>(this IEnumerable<TSource> sources, Func<TSource, TKey> keySelector, bool byDesc)
=> byDesc ? sources.OrderByDescending(keySelector)
: sources.OrderBy(keySelector);
public static IOrderedEnumerable<TSource> Then<TSource, TKey>(this IOrderedEnumerable<TSource> sources, Func<TSource, TKey> keySelector, IComparer<TKey> comparer, bool byDesc)
=> byDesc ? sources.ThenByDescending(keySelector, comparer)
: sources.ThenBy(keySelector, comparer);
public static IOrderedEnumerable<TSource> Then<TSource, TKey>(this IOrderedEnumerable<TSource> sources, Func<TSource, TKey> keySelector, bool byDesc)
=> byDesc ? sources.ThenByDescending(keySelector)
: sources.ThenBy(keySelector);
オプション引数 byDesc
を設定しました。これにより、次のようにコードを書けます。
// ラップ前
var hoge = GetEmployee.OrderBy(s => s.Age).ThenByDesc(x => x.Salary);
// ラップ後
var hoge = GetEmployee.Sort(s => s.Age).Then(s => s.Salary, byDesc: true);
もちろん引数名の byDesc
を省略することもできますが、理由があり記載しています。理由については、以下の記事で解説しているので、よかったらご覧ください。
比較
メソッド名をラップしてみることで、冒頭のコードは次のように書けるようになりました。
// 変更前
var hoge = new[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
var fuga = hoge.Where(x => x % 2 == 0).Select(x => x * 2).OrderBy(x => x).ToArray();
// 変更後
var hoge = new[] { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
var fuga = hoge.Filter(x => x % 2 == 0).Map(x => x * 2).Sort(x => x).ToArray();
LINQ にあまり慣れていない人でも、よりスッと入ってきやすいコードになったのではないでしょうか。
その他
ブログとCrieitでも記事を公開しているので、よければご覧ください。
ブログ:BOYISH KINGDOM
Crieit:apashoni