2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C#の遅延実行の基本

Posted at

はじめに

遅延実行とは
プログラムの処理をすぐには実行せず、結果が必要になるまで「後回し」にする仕組み
どういうタイミングで実行されているのか、イメージしづらかったため、記事に起こして整理していく

なぜ知っておくべか

メモリ効率の向上
大量のデータを扱う場合、すべてのデータを一度にメモリは読み込む必要がなくなり、必要な分だけ処理するため、メモリ使用量を抑えることができる

パフォーマンスの最適化
必要のない処理をスキップできる
例えば、最初の10件だけ必要な場合取得して、残りのデータは処理しないとか

遅延実行と即時実行

C#のLINQには今すぐ実行される「即時実行」と後から実行される「遅延実行」の2つのパターンが存在する

遅延実行

以下コードは遅延実行を使ったサンプルの実装
任意の配列で作られた数字がある
Where()を使って、偶数を取得して、それを表示させるというプログラム
Where()ではただ、「偶数を取り出す」という計画を作っただけで、実際の処理はforeachで使うときに実行される

test.cs
var numbers = new int[] { 1, 2, 3, 4, 5 };

var evenNumbers = numbers.Where(n => n % 2 == 0);

// この時点ではまだ、フィルタリングは実行されていない

// フィルタリングが実行される
foreach (var number in evenNumbers)
{
    Console.WriteLine(number);
}

即時実行

ToList()をつけることで、フィルタリングされたものがすぐに実行され、反映される

test.cs
// 即時実行
var evenNumberList = numbers.Where(n => n % 2 == 0).ToList();

foreach (var number in evenNumberList)
{
    Console.WriteLine(number);
}

遅延実行の仕組み

裏側ではC#の「イテレータ」という機能使われている

test.cs
public static IEnumerable<int> GetEvenNumbers(int[] numbers)
{
    foreach (var n in numbers)
    {
        if (n % 2 == 0)
            yield return n; 
    }
}

このyield returnが遅延実行の鍵となる
foreachなどで値が要求されるたびに、次の条件に合う値を1つずつ返す
全ての値を一度に処理して配列を作るのではなく、必要なときに必要な分だけ処理する

実際に動きを確認出来る例

test.cs
// 遅延実行の様子を視覚的に確認
var numbers = new[] { 1, 2, 3, 4, 5 };

Console.WriteLine("クエリ作成");
var query = numbers.Where(n => {
    Console.WriteLine($"Where: {n}を処理中");
    return n % 2 == 0;
});

Console.WriteLine("foreach開始");
foreach (var item in query)
{
    Console.WriteLine($"結果: {item}");
}

結果

クエリ作成
foreach開始
Where: 1を処理中
Where: 2を処理中
結果: 2
Where: 3を処理中
Where: 4を処理中
結果: 4
Where: 5を処理中

Whereの処理がforeachの中で行われており、遅延実行がされていることが確認できる

どういうケースで遅延実行を使うのか

  1. 大量データを扱う場合
  2. I/O操作(ファイルなど)の効率化

どういう点を気をつけるべき

副作用
後から何か書き換えたりすることがある処理については、想定外の結果になるので気をつける

test.cs
// 危険な例
int counter = 0;
var query = numbers.Select(n => counter++); // counterは後で増加

counter = 100; // ここでcounterを変更

// 実行されるのはここ - counterは100から始まる!
foreach (var item in query)
{
    Console.WriteLine(item); // 想定外の結果になる
}

まとめ

遅延実行は「今すぐやる」というよりも、「必要になった時必要な分だけやる」という考え方
何も考えずにLINQを使っていると、思わぬところで想定外のことが起きるかもしれない
遅延実行を使った方がいいケース以外の時は、ToList()などを使って即時実行してた方が安全だと思った

2
4
1

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
2
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?