0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

LINQ のメソッド名が SQL っぽいからラップする

Last updated at Posted at 2022-06-06

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 なので、それぞれ SortSortByDesc というメソッド名でラップしましたが、ここは昇順か降順かをオプション引数で切り替える Sort という名前のメソッドにしても良いでしょう。

「昇順か降順かをメソッド名だけで判断できる」という利点はありますが、同じようなことが別の名前のメソッドで定義されているのも、個人的には気持ち悪いと感じます。

同様に、ThenByThenByDescendingThen というメソッドでラップしてみます。

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

0
2
26

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?