C#のyieldは,なんでiteratorの中でしか使えんのだ

C#2.0はyieldをサポートしていたなぁそういえば,とか思って調べてみると,なんかiteratorの中でしか使えないらしい.しょうがないのでちょっと騙すようなコードを書いて,過去に作ったManaged DirectXの上で動作するライブラリ上で動かしてみた.簡単に言えば,IEnumable.GetEnumerator()の返したオブジェクトを保存し,毎フレームごとにMoveNext()を呼んでタスクを動かして状態を更新する.
ただし,

  • yieldがどんな実装になってるか知らないので,どこまで外の変数をさわるとかの副作用が許されるのかはよくわからない.
  • 返す値が同じだからって,JITで吹っ飛んだりはしないよなぁ.
  • オーバーヘッドは度外視.
public abstract class YieldableSprite: CTSprite //CTSpriteはスプライトクラスで,適当に描画機能がある
{
    IEnumerator enumerator;
    protected delegate IEnumerable TaskType(object arguments);
    public CTYieldableSprite(DXTexture tex, int srcx, int srcy, int width, int height)
        : base(tex, srcx, srcy, width, height)
    {
    }
    protected void SetTask(TaskType task, object argument)
    {
        enumerator = task(argument).GetEnumerator();
    }
    public new void Render()
    {
        enumerator.MoveNext();
        base.Render();
    }
}

で,この基底クラスの上に適当に実装

public class MyYieldableSprite: YieldableSprite
{
    public MyYieldableSprite(DXTexture tex, int srcx, int srcy, int width, int height)
        : base(tex, srcx, srcy, width, height)
    {   
        //初期設定
        Velocity.X = 1;
        Velocity.Y = 1;
        SetTask(task1,null);
    }
    protected System.Collections.IEnumerable task1(object n)
    {
        while (true)
        {
            RotRadius += 0.01f; //ちょっと回転

            //画面からはみ出たときに速度を変更する
            if (this.Position.Y > 480-this.Height) this.Velocity.Y = -1;
            else if (this.Position.Y < 0) this.Velocity.Y = 1;
            if (this.Position.X > 640-this.Width) this.Velocity.X = -1;
            else if (this.Position.X < 0) this.Velocity.X = 1;

            yield return 0;
        }
    }
}

とこんな感じで書いて,画面中を回転しながらはね回るオブジェクトが書けた.よかったのう.こういう事をするためのクラスがC#にないというのが微妙に不思議.
追記:このソースではいまいちyieldの恩恵がないことに後で気づいたorz.こういったサンプルの方がいいかもしれない.

protected System.Collections.IEnumerable task1(object n)
{
    while (true)
    {
      for(int i=0;i<60;i++){ this.Velocity.X=0; this.Velocity.Y=1; yield return 0;}
      for(int i=0;i<60;i++){ this.Velocity.X=1; this.Velocity.Y=0; yield return 0;}
      for(int i=0;i<60;i++){ this.Velocity.X=0; this.Velocity.Y=-1; yield return 0;}
      for(int i=0;i<60;i++){ this.Velocity.X=-1; this.Velocity.Y=0; yield return 0;}
    }
}

これで一応四角形にぐるぐる回るスプライトになる.

さらに追記:実装悪かったので少し修正した.