【LINQ】Selectさえ使えればLINQは大体OK

C#

こんにちは、働くC#プログラマーのさんさめです。

LINQの大前提と言っても良い
Selectの紹介です。

Selectは、
LINQを語る上では絶対に外せない、
超重要メソッドです。

しかし、慣れないうちは
Selectでもできることを
foreachで書いてしまったりしがちです。

逆に言えば、
Selectを適材適所で使えるようになったら、
LINQは使えているといっても過言ではありません。

Selectを常用するようになると
自然と他のLINQメソッドが使いたくなり
どんどん知識を仕入れられるのですね。

本記事では、
Selectでできることを説明しつつ、
実際に「使えるケース」
にはどんなものがあるかを紹介します。

スポンサーリンク

Selectの概要

Selectメソッドは、
引数で与えられたメソッドに従って
リストの各要素を新しい要素に置き換えます。

メソッドを引数で与える…?

初めてLINQを触る人は、
メソッドを引数で与えることについて、
直感的に理解しにくいかもしれません。

たとえば、「2倍する」とか、
「文字列にする」
「そのパスでファイルロードする」
といった処理自体を渡す…ということです。

例えば次のサンプルは、
数値のリストに対して「2倍する」の
メソッドを与えたSelectの使い方になります。

var intArray = new[] { 0, 1, 2, 3, 4 };
var selectEnumerable = intArray.Select(x => x * 2);
// 0 2 4 6 8

コード中の「x => x * 2」が、
「2倍する」の部分になります。

Selectの括弧の中に「x => x * 2」
が書いてある!

これが
「メソッドを引数で与える」ってことか

「文字列にする」のサンプルも見てみましょう。

var intArray = new[] { 0, 1, 2, 3, 4 };
var selectEnumerable = intArray.Select(x => x.ToString("00"));
// "00" "01" "02" "03" "04"

「x => x.ToString(“00”)」という部分で、
2桁の文字列に変換しています。

このように、
処理自体を渡すことで
Selectはリストの各要素に同じ処理を
網羅的にかけることができます。

Selectの最初の使い道は無駄なコードを減らすこと

さて、Selectを使う場面について考えてみましょう。

先ほどのサンプルは、
当然foreachで書き下すこともできます。

しかし、foreachで書こうと思うと、
次のように別のリストを用意するか、
forで書いて元の配列に破壊的変更を加えるしかありません

var intArray = new[] { 0, 1, 2, 3, 4 };
var intList = new List<int>();
foreach (var intValue in intArray)
{
    intList.Add(intValue * 2);
}

for (var i = 0; i < intArray.Length; i++)
{
    intArray[i] = intArray[i] * 2;
}

無駄な変数が増え、
行数やインデントも増やすことになります。

後者はそもそも処理の意味が変わってしまいますね。

このような時は、
Selectを使うようにするだけで、
グッと理解しやすいコードになります。

やりたいことがそのままコードに書いてある
状態になるからですね。

ただし、1つのSelect文で多数の処理を
書こうとすると逆に理解しにくいコードになるので
その点は注意しましょう。

多数の変換を施す場合は複数のSelectを繋ぐ形に
分けるのも手です。

var intArray = new[] { 0, 1, 2, 3, 4 };
var selectEnumerable = intArray.Select(x =>
    {
        var x2 = x * 2;
        var str = x2.ToString("00");
        var myClass = new MyClass(str);
        return myClass;
    });

var selectEnumerable2 = intArray
    .Select(x => x * 2)
    .Select(x => x.ToSTring("00"))
    .Select(x => new MyClass(x));

処理コストが多少嵩んだとしても、
読みやすいコードの方が価値があると考えます。
(本当にシビアな場合は、
そもそもLINQの使用判断から
見直すことになります)

実際に「使える」ケースの紹介

Selectの基本的なことについて説明しましたが、
私が実務上でよく使っているケースを紹介します。

モデル層のリストをビューモデル層のリストに変換する

ありがちなのが、
モデル層がリストになっていて、
それを貰って、
ビューモデルのリストに変換する処理です。

次のサンプルはとても色々省略したコードですが、
便利さは伝わるのではないかと思います。

public List<ViewModel> ViewModels { get; }

public MainWindowViewModel(ICollection<Model> models)
{
    this.ViewModels = models
        .Select(x => new ViewModel(x)) // SelectでViewModelのクラスに変換し
        .ToList(); // ToListでリスト化してプロパティに代入
}

もはやforeachがどこにもありません。

複数の情報をメッセージダイアログに表示する

string.Joinを使ってサクッと複数行のデータを表示
でも紹介している、
「複数のクラスの情報から、
改行アリの1つの文字列を作る」
ときに使える方法です。

var message = string.Join("\n", list.Select(x => x.Name + x.Id.ToString()));
MessageBox.Show(message);

非常に短い記述で処理を完成させることができます。

より詳しくは、
string.Joinを使ってサクッと複数行のデータを表示
をご覧ください。

ファイルパスに関する操作

あるフォルダにあるパスを列挙して、
拡張子だけのリストを取得する
といったコードを書くときにも、
やはりSelectが使えます。

var files = System.IO.Directory.EnumerateFiles(@"C:\Program Files");
var exts = files.Select(x => System.IO.Path.GetExtension(x));

どうでしょうか。
Selectの使用場面は実はごく身近にありふれているのですね。

副作用のあるコードや、遅延評価であることに注意

【LINQ】Whereを使ってガード節continueをコードから消す
で説明している、
「副作用のあるコード」や
「遅延評価」については、
Selectメソッドでも変わらず注意が必要です。

詳しくは上記の記事の後半を読んでくださいね。

しかしながら、
LINQはこれらの懸念点を
補って余りあるメリットがある
強力な仕組みです。

最初はWhereとSelectだけでも良いので、
ぜひ、積極的に使ってみてください。

まとめ

まとめです。

  • LINQ拡張メソッドのSelectを紹介
  • Selectはリストの各要素を
    指定した処理に基づいて変換するメソッドである
  • Selectの最初の使い道はコードの記述量を減らして
    可読性を上げること
  • いくつかの使用実例を紹介

最後までお読みいただき、ありがとうございました。

コメント

タイトルとURLをコピーしました