気まぐれメモランダム / でたらめフィードバック

ちょっと楽するLINQの書きかた

公開: / 最終更新日:

最近他の人の書いたメソッド構文のLINQを読む機会が増えて、もっとシンプルに書けるのになあと思うことがしばしば。典型的には、LINQで絞り込んだ IEnumerable<T> に対して Enumerable.ToList<TSource>(IEnumerable<TSource>) メソッド を呼び出して List<T>クラス のインスタンスを生成、その後 List<T>.ForEach(Action<T>) メソッド で取り出した各要素を別のリストに List<T>.Add(T) メソッド で追加したりするような処理です。

LINQの処理はイテレーターをベースにしています。一方List<T>クラスは要素をオンメモリで管理します。そのためイテレーターで処理できるデータ(LINQ to Entitiesでアクセスしたデータなど)はなるべくList<T>クラスのインスタンスにしないほうが効率的に処理できます。List<T>.ForEach(Action<T>)メソッドがList<T>クラスにしか定義されていないのはそういう理由もあります。

実際わざわざList<T>クラスのインスタンスを生成してList<T>.ForEach(Action<T>)メソッドを呼び出さなくても、 IEnumerable<T>インターフェースをそのまま参照できるforeach文を使えばLINQのベストプラクティスに則った効率的な処理になります。

もう一歩踏み込めば、各要素にアクセスする目的が他のリストへの要素の追加であれば、List<T>.ForEach(Action<T>)メソッドやforeach 文を使うまでもなく、List.AddRange(IEnumerable<T>) メソッド呼び出しで十分なことも多いでしょう。LINQ導入時に行われたクラス / インターフェース / メソッドの整理で、集合を対象とするメソッドの多くには IEnumerable<T>インターフェースを引数に指定できるオーバーロードが追加されています。
文脈によってはもっと整理できるかもしれません。

あとこれはあまり知られていないようなのですが、Enumerable.Single メソッドEnumerable.FirstOrDefault メソッドのような要素取り出し系のメソッドには多くの場合条件指定用関数を引数とするメソッドのオーバーロードが存在します。Enumerable.Where メソッドで絞り込んでからEnumerable.FirstOrDefaultメソッドで要素を取り出す記述も見かけますが、Enumerable.Whereメソッドなしで済むこともよくあります。要素の存在を確認するEnumerable.Any メソッドなども同様です。

2024-08-10(Sat)追記: 要素の有無確認のために Enumerable.ToList<TSource>(IEnumerable<TSource>) メソッドList<T> クラスのインスタンスを生成してから List<T>.Count プロパティ0 かをチェックするコードも見かけますが、Enumerable.Any メソッド を使えばリストのインスタンスは不要です。

LINQのメソッド構文は定型的に解説されることも多いでしょうから各種オーバーロードのバリエーションを知る機会が少ないということはあるかもしれません。もったいない話なので、書名のバージョン的には古くなってしまっていますが、C#を使われる方には『Effective C# 6.0 / 7.0』『More Effective C# 6.0 / 7.0』の一読をお勧めします。LINQの基礎は導入から変わっていないので各種知見はいまでも有効ですし、LINQ以外でも学べる点が多いでしょう。コードを楽に書けるヒントがきっとつかめることと思います。

以上、参考になりましたら幸いです。

関連コンテンツ

Pick up work

最近のエントリ

アーカイブ

ブログ情報