http://blogs.wankuma.com/rti/archive/2008/02/12/122771.aspx
クエリー式とイテレータ
これの続きっちゃー続きなんですが、テーマはちょっと違うのでタイトルは別にしました。
先日のデブサミで、通りすがりの NyaRuRu さんと上記のエントリーに関する話をしていた時に、教えてもらった
イテレータの後始末
の話です。
まず、イテレータの動きです。以下のコードと、その動きを見れば明らかです。
public void Test() {
foreach(var x in Range(0, 3)) Console.WriteLine(x);
}
public IEnumerable<int> Range(int start, int count) {
Console.WriteLine("素敵だし爽やかRさん");
for(var i = start; i < start + count; ++i) yield return i;
Console.WriteLine("わんくま同盟の若きホープ");
}
【実行結果】
素敵だし爽やかRさん
0
1
2
わんくま同盟の若きホープ
つまり yield で一旦呼び出し側に制御を返した後に再度呼ばれたら、その続きから実行するという動きをします。
じゃあ、以下のように中断したらどうなるでしょう?
public void Test() {
foreach(var x in Range(0, 3)) {
Console.WriteLine(x);
return;
}
}
【実行結果】
素敵だし爽やかRさん
0
つまり、Rさんが「素敵だし爽やか」であることはわかりますが「わんくま同盟の若きホープ」であることまではわかりません。大問題です!
他にも Range メソッド内でアンマネージリソースを使っている場合の解放に関しても、少しだけ問題が生じます。
解決策として思い浮かぶのは、try - finally です。
static public IEnumerable<int> Range(int start, int count) {
try {
Console.WriteLine("素敵だし爽やかRさん");
for(var i = start; i < start + count; ++i) yield return i;
}
finally {
Console.WriteLine("わんくま同盟の若きホープ");
}
}
【実行結果】
素敵だし爽やかRさん
0
わんくま同盟の若きホープ
今度は、Rさんが「素敵だし爽やか」で「わんくま同盟の若きホープ」であることがわかります。
try - finally の技は、using ステートメントが登場するまでは当然のように使われていましたが、イテレーターブロックでも正しく動作するというのは、さすが C# というところでしょうか。