IEnumerator, IEnumerable を使い処理ルーチンを書く

時間のかかる処理を1つのメソッドに書いてしまうと,計算途中結果を見たいときや,計算途中結果次第では処理を中断したいときに困ります.一方で,処理結果を中断できるようなコードをわざわざ書くのも面倒です.
そこでIEnumeratorを使い,処理結果を都度取得して,いつでも処理を中断できるコードを書いてみました.

コード

長々説明を書くよりもコードを見たほうが早いでしょう.
コンテキスト キーワード yield - C# リファレンス | Microsoft Docs

計算結果が出るたびに yeild で結果を返します.処理が終了したならば yeild break; を呼びます.
外部ではforeachでその都度結果を取得できます.

まるで,はじめから処理結果が1つの配列にすべて入っているかのように扱えるところが面白いです.

これはIENumerableを使いましたが,IEnumeratorを直接実装してもよいです.その場合には,Reset(), Dispose()メソッドも実装することになるので,それらのメソッドを利用する意味があるならば,IEnumeratorの直接実装のほうがメリットがあるでしょう.

// 処理を実行するクラス
public class job_class
{
  // 変数宣言
  int _cnt = 0;
  IEnumerator<double> _job;

  // プロパティ
  public IEnumerator<double> Job
  {
     get { if(null == _job) _job = createEnumerable().CreateEnumerator();
           return _job;           
         }
  }

  //処理メソッド
 IEnumerable<double> createEnumerable()
 {
 // 一見無限ループだが, yeild で制御が外部に移る
 // メソッド内部で宣言した変数は,yield呼出しごとにクリアされるので,処理に必要な変数はインスタンス変数にする.
 while (true)
   {
     if(_cnt >= 10000)
       yield break; // 処理終了

     System.Threading.Thread.Sleep(10);
     _cnt++;
     _value = _cnt * (_maximum - _minimum) / 10000 + _minimum;

     _message = String.Format("{0}秒待ち 完了", _cnt / 100);
     OnPropertyChangedEvent(string.Empty);

     yield return _value; // 計算結果を返す
   }
 }
}

public class job_execution
{
  public void some_method()
  {
    job_class job = new job_class();
    foreach(double result in job.Job)
    {
      //計算結果を都度受け取れる
      //処理を中断したければ, ここで break してforeachを出ればよい
    }
  }
}

コード全部

サンプルコードを掲載します.
Microsoft Visual Studio 2008 と Microsoft .NET Framework Version 3.5 で書いています.