C# の yield return の使い道

突然ではありますが、とっておきの yield return の使い道を紹介しようぢゃないか。・・・って別にそんなたいそうなものじゃないけどね。

まず、IEnumerator ってのは次のような状態遷移モデルで捉えることが出来るわけだ。

逆に考えると、状態遷移でモデル化できる設計は、IEnumerator を利用して実装できるんじゃないか、というのが着想。つまり、yield return を利用して状態遷移モデルを実装しよう、ということ。

何のメリットがあるかって?

僕が思うに、状態遷移モデルは結構実装が面倒だ。面倒というか、コードがぐちゃぐちゃになりやすい。ところが yield return を上手く使えばあら不思議、状態遷移がすっきりと記述できてしまうのだ。

具体例を挙げてみる。

何か作図ツールのようなものを開発するとしよう。CADとかパワーポイントとかVisioとかを想像すればよい。さあ、まずは直線作図コマンドの開発だ。ビューを2点クリックされたら、その2点を結ぶ直線を作図する機能を開発するのだ。

このコマンドは、次のような状態遷移図でモデル化できる。

さあ、どうやって実装する?

クリックイベントを検知しなくちゃならないのだから、まずは Control の MouseClick イベントにハンドラが必要になるぞ。クリックイベントは1点目と2点目の2種類が必要だから、うーん、どうしようか、クリックされた回数をカウントするint型のカウンタ変数を用意して、カウンタが奇数だったら1点目のクリック、偶数だったら2点目のクリックと判断することにしようか。あ、待てよ、そもそも直線作図コマンドが起動されている状態かどうかを表すためのbool変数も必要になりそうだな。・・・

ってなことになるわけでして、上の状態遷移図とは似ても似つかないコードになってしまいがちだ。更にここに、円弧作図の機能も追加しよう、スプライン曲線も必要だ、と機能を追加したらどうなるか。素敵なスパゲッティコードの出来上がりだ。

そう、「状態遷移」と「イベントドリブン」のコンボはスパゲッティコードの温床だと思うわけですよ。

そこで yield return の出番というわけ。次のように書くのだ。

IEnumerator<状態> 直線作図()
{
  yield return クリック待ち状態;  // 1点目のクリック待ち
  yield return クリック待ち状態;  // 2点目のクリック待ち
  直線オブジェクトを生成;
}

これならクリック回数をカウントするような妙な変数を用意する必要がない。何らかの変数が必要になったとしても、その変数はこの関数のローカル変数として作成すれば済む話。関心の分離、万歳!

これは実は、拙作のOpenGLフレームワーク「ヒスイ」(http://www.quatouch.com/products/hisui.html)で実現されている。興味がある方は例えばチュートリアルこの辺りを読んでみて欲しい。

CADアプリ以外の分野でも、状態遷移とイベントドリブンのコンボで苦しんでいる分野って、結構あるんじゃないかと思ってみたり。よく知らないけど。そんなとき、yield return を思い出せば幸せになれるかも。

追記 2008-09-10

このモデルの実装サンプルコードはこちら。
C# でイベントハンドラを yield return してみよう - カタチづくり