連載:[完全版]究極のC#プログラミング

Chapter3 新しい繰り返しのスタイル ― yield return文とForEachメソッド

川俣 晶
2009/08/31

3.8 自動的に作られるオブジェクトと二重利用

 反復子ブロックはブロックではないので、直感に反する動作を行うように見えることがある。

 たとえば、次のリスト3.7のように、たった1つの反復子ブロックを二重の繰り返しで使うと、1つのメソッドが内側のループと外側のループで共有されるため、正常にカウントされないように思えるかもしれない。ところが、このコードは、内側と外側の繰り返しが別個に、かつ正常にカウントされるのである。

using System;
using System.Collections.Generic;

class Program
{
  private static IEnumerable<int> getCounter(int from, int to)
  {
    for (int i = from; i <= to; i++) yield return i;
  }

  static void Main(string[] args)
  {
    IEnumerable<int> range = getCounter(0, 2);

    foreach (int j in range)
    {
      foreach (int i in range)
      {
        Console.Write("({0},{1}) ", i, j);
      }
      Console.WriteLine();
    }
    // 出力:
    // (0,0) (1,0) (2,0)
    // (0,1) (1,1) (2,1)
    // (0,2) (1,2) (2,2)
  }
}
リスト3.7 二重に反復子ブロックを使った例

 この場合、ソースコードを見ると列挙を行うオブジェクトは、getCounterメソッド呼び出しによって1つだけ生成され、そのオブジェクトはIEnumerable<int>インターフェースを実装しているので変数rangeに代入された……というふうに見えるかもしれない。そのため、列挙を行うオブジェクトは暗黙的に1つだけ作られて共有された……と見えるかもしれない。

 しかし、実際はそうではない。

 列挙可能インターフェース(IEnumerable<T>)は「列挙子ファクトリ」という機能を持つ。つまり、このインターフェースは、列挙を行うオブジェクト(列挙子)を作り出すファクトリ(作成工場)として機能する。

 そのため、「foreach (int i in range)」といったコードは、変数rangeを通じて列挙オブジェクトを取得しようとする作業の過程で新しい列挙オブジェクトを作り出すのである。このソースコードにはこういったコードが2つあるため、実行時には列挙オブジェクトは2つ生成される。

 つまり、

反復子ブロック j 1つの列挙子ファクトリ j 2つの列挙オブジェクト

という流れになる。その結果として、内側と外側のforeach文は別の列挙オブジェクトを使い、別々にカウントが行われる。


 INDEX
  [完全版]究極のC#プログラミング
  Chapter3 新しい繰り返しのスタイル ― yield return文とForEachメソッド
    1.3.1 「繰り返し」という古くて新しい問題
    2.3.2 数を数えるというサンプル
    3.3.3 C# 1.xによるRangeクラスの実装
    4.3.4 C# 3.0によるRangeクラスの実装
    5.3.5 yield break文による中断
    6.3.6 yieldは予約語ではない
    7.3.7 1つのクラスに複数の列挙機能を付ける
  8.3.8 自動的に作られるオブジェクトと二重利用
    9.3.9 catchできない制約
    10.3.10 制約の真相―見た目と違う真実の姿
    11.3.11 ForEachメソッドを使う別解
    12.3.12 性能比較
 
インデックス・ページヘ  「[完全版]究極のC#プログラミング」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間