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

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

川俣 晶
2009/08/31


 本記事は、(株)技術評論社が発行する書籍『[完全版]究極のC#プログラミング ― 新スタイルによる実践的コーディング』から、許可を得て転載しています。
 同書籍は、もともと本フォーラムにて連載していた『C# 2.0入門』、『C# 3.0入門』の記事を整理統合し、加筆、修正されたものです。

  手元でまとめて読みたい方は、ぜひ書店などにてお買い求めください。

 【注意】本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどは@ITのそれとは一致しません。あらかじめご了承ください。

3.1 「繰り返し」という古くて新しい問題

 本章を書くに当たってあらためて考えてみると、プログラミングにおいて「繰り返し」とは古くて新しい問題であることが痛感される。

 たとえば、8ビットワンボードマイコンの全盛期、繰り返しはレジスタの値を1つずつ減らしてゼロになったら(ゼロフラグが立ったら)終わるという構造が多かった。ところが、最も人気のあった8080AやZ80といったCPUは、8ビット単位でレジスタの値を1減らす命令(DCR/DEC)ではゼロフラグが変化したが、16ビット単位でレジスタペアの値を1減らす命令(DCX/DEC)を実行してもゼロフラグが変化せず、ゼロ判定のために別途コードを書く面倒が必要だった。かといって、ライバルのモトローラ6800などでは、条件付きジャンプは8ビット範囲の相対アドレスでしか飛べなかったので、ちょっとコードを書き足すとループの最初に戻れなくなってアセンブルエラーということもあった。

 ともかく、繰り返しは頻出するわりにいろいろな面倒をもたらしてくれた。しかも、つねに進歩し続けているわけではなく、揺り戻しも起こる。

 たとえば、コレクションの列挙といえば、Visual BasicならFor Each文を使って簡単に書けたにもかかわらず、後から登場したはずのJavaはfor文と列挙オブジェクトを併用して、手動でFor Each文相当のコードを書くことを要求した。もちろん、それでは実用的ではないので、後からFor Each文相当の構文が導入されたが、明らかに動きが遅い。

 それとは別に、自由度の拡大がワナを増やすという事例もある。Visual BasicのFor文で数字をカウントする場合、

For i = 0 To 9
   …
Next i

といった表記となり、必ず特定の変数(この場合はi)を使って、必ず1ずつ増えながら、指定された数の範囲を数えることしかできなかった。

 それに対して、C言語とその構文を引き継ぐ言語のfor文はきわめて自由度が高い。for文の内部に書き込む3つの式(「初期化」、「継続条件」、「繰り返し」に当たる)を自由に書くことができるので、単純に変数が増減するという以外の繰り返し、たとえばファイルが終わるまで連続して読み込むといった繰り返しを書くことができる。

 だが、自由度の高さは単純なミスを自動的に排除できないことを意味する。たとえば、次のようなバグ(変数iと間違えてjと書いている)はコンパイル時に確実に排除できない。

for ( i = 0; i <= 9; j++ )
{
   …
}

 さて、C# 1.xは、自由度の高いC言語ふうのfor文と、自由度は低いが安全かつ楽ができるforeach文を持ち、わりとバランスの良い言語だったといえる。筆者の個人的な感想からいえば、繰り返しのかなりの割合はforeach文で記述でき、残ったイレギュラーな繰り返しはfor文やdo文/while文で十分に対処できた。

 ただし、C# 1.xの「わりと使えるバランスの良さ」には1つの大きな落とし穴があった。それは、foreach文で列挙可能なオブジェクトを作るのが少々面倒くさい……という問題である。

 クラスライブラリのオブジェクトを使って列挙しているだけならあまり気にならないが、自作クラスを列挙可能にしようと思うと、大きな面倒と手間を要求されてしまったのである。

 C# 3.0はその問題に対処するために「反復子」という機能を導入している。本章は主にそれについて解説する。


 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 記事ランキング

本日 月間