TIPSLINQは便利だが、使い方を間違えると大量のメモリを消費してしまう場合がある。本稿では、LINQを使用する場合に消費するメモリ量を抑制する方法を解説する。本稿では、最もよくある失敗例と、その対策を説明する。事前準備プログラムが使用しているメモリのサイズをコンソールに表示するメソッドを作っておく(次のコード)。Sub使用しているメモリサイズを表示するメソッドの例(上:C#、下:VB)GCクラス(System名前空間)のGetTotalMemoryメソッドは、現在割り当てられていると考えられるバイト数を取得する。このメソッドを呼び出すと、その時点でプログラムが使用しているメモリサイズの概数がMBytes単位でコンソールに表示される。ある失敗は、LINQで処理している途中でEnumerableクラス(System.Linq名前空間)のToList拡張メソッドやToArray拡張メソッドを使ってしまうことだ。次のコードは100万個の整数の中から偶数だけの合計を求めるものだ。処理の途中でToList拡張メソッドを使っているため、その都度新しいコレクションが「実体化」され、無駄にメモリを消費してしまう。消費するメモリ量によっては、プログラムの応答にも悪影響を及ぼすだろう。出力例→14.0MBytes途中で新しいコレクションを「実体化」させてしまう、悪いコードの例(上:C#、下:VB)これはよくない例である。このように途中でToList拡張メソッドを使うと、そこで新しいコレクションが「実体化」されてしまい、無駄にメモリを消費する(筆者の環境では、コメントに書いたように10MBytesほど増加した)。このコードは、偶数だけを取り出すためにEnumerableクラスのWhere拡張メソッドを使っている。int/IntegerのままSum拡張メソッドに渡すとオーバーフローしてしまうので、渡す前にSelect拡張メソッドを使ってlong/Longへキャストすることでそれを避けている*1。冒頭で使っているEnumerableクラスのRangeメソッドは、引数に指定した範囲の整数シーケンスを生成してくれる(第1引数は開始数、第2引数は生成数)。コレクションのインスタンスを作成し、そこに「1」から「1,000,000」(=100万)までの整数を追加することと同じである。この他に、Enumerableクラスの拡張メソッドを使用するため、System.Linq名前空間のインポートが必要だ。ここでWhere拡張メソッドとSelect拡張メソッドの引数に記述してあるのは、ラムダ式である。ラムダ式について詳しくは、次のMSDNのドキュメントを参照していただきたい。Basic)LINQの処理中に使うメモリを節約するには?ToList拡張メソッドやToArray拡張メソッドなどを、可能な限り途中で使わないようにすればよい。すれば無駄にコレクションを「実体化」せずに済み、メモリを節約できるし、大量のメモリを確保するための応答速度低下も避けられる。上のコードからToList拡張メソッドを取り除くと、次のようになる。出力例→4.1MBytes途中で新しいコレクションを「実体化」させない、よいコードの例(上:C#、下:VB)この例では、ToList拡張メソッドを取り除くことで、使用メモリの増加は全くなくなった。この他に、Enumerableクラスの拡張メソッドを使用するため、System.Linq名前空間のインポートが必要だ。これはLINQの大きな特徴であって、IEnumerable<T>インターフェース(System.Collections.Generic名前空間)のまま扱う限りは、無駄にメモリを消費するコレクションを生成しないようになっているのである*2。この機能は「クエリの遅延評価(または遅延実行)」と呼ばれる。プロジェクトをサポートする言語機能」の項中、「クエリの遅延評価」の項を参照していただきたい。メールマガジンをご購読ください。申し込みページへITmediaはアイティメディア株式会社の登録商標です。