yieldを使いました(2)

前回,検証結果を書くとか言っておきながら,全然出来てませんorz
ちょっと仕事が立て込んだり付き合いがあったりというのもあるんですが,それとは別にyieldって難しいなーって思ったり.いまようやく.NET Reflectorを使ってyieldの仕組みを見始めたところ,なかなか深遠な世界が広がってますね.
激しく書きかけのまま終わりそうな予感があるんですが,とりあえず今見ているところを書きます.


GetBytesメソッドを再掲します.

static IEnumerable<byte[]> GetBytes(Stream stream)
{
  using (BinaryReader binaryReader = new BinaryReader(stream))
  {
    byte[] buffers = new byte[1024];
    int readnum = 0;
    while ((readnum = binaryReader.Read(buffers, 0, buffers.Length)) > 0)
    {
      byte[] returnBuf = new byte[readnum];
      Array.Copy(buffers, returnBuf, readnum);
      yield return returnBuf;
    }
  }
}

Reflectorをかけると,d__0というクラスが出来上がるようです.なんかが総称型っぽいですが,関係あるのか不明.

private static IEnumerable<byte[]> GetBytes(Stream stream)
{
  <GetBytes>d__0 d__ = new <GetBytes>d__0(-2);
  d__.<>3__stream = stream;
  return d__;
}

d__0を見てみましょう.

[CompilerGenerated]
private sealed class <GetBytes>d__0 : IEnumerable<byte[]>, IEnumerable, IEnumerator<byte[]>, IEnumerator, IDisposable
{
  // Fields
  private int <>1__state;
  private byte[] <>2__current;
  public Stream <>3__stream;
  public BinaryReader <binaryReader>5__1;
  public byte[] <buffers>5__2;
  public int <readnum>5__3;
  public byte[] <returnBuf>5__4;
  public Stream stream;

  // Methods
  [DebuggerHidden]
  public <GetBytes>d__0(int <>1__state);
  private bool MoveNext();
  [DebuggerHidden]
  IEnumerator<byte[]> IEnumerable<byte[]>.GetEnumerator();
  [DebuggerHidden]
  IEnumerator IEnumerable.GetEnumerator();
  [DebuggerHidden]
  void IEnumerator.Reset();
  void IDisposable.Dispose();
  // Properties
  byte[] IEnumerator<byte[]>.Current { [DebuggerHidden] get; }
  object IEnumerator.Current { [DebuggerHidden] get; }
}

CompilerGeneratedとかDebuggerHiddenといった属性は,やっぱり不明.CompilerGeneratedはきっとコンパイラが自動生成したものですよ,ってことだろうと想像できるけど,DebuggerHiddenは?
それはさておき,このクラスのなかの<>1__stateというのがキモっぽいなってことは,なんとなく分かります.GetBytesメソッドの,new d__0(-2); でコンストラクタの引数に-2を設定していますが,この-2は<>1__stateに設定されます.いちおうソースコードも貼り付けましょうか.

[DebuggerHidden]
public <GetBytes>d__0(int <>1__state)
{
  this.<>1__state = <>1__state;
}

ここからだんだん難しくなります.次にGetEnumeratorを見てみましょう

[DebuggerHidden]
IEnumerator IEnumerable.GetEnumerator()
{
    return this.System.Collections.Generic.IEnumerable<System.Byte[]>.GetEnumerator();
}
[DebuggerHidden]
IEnumerator<byte[]> IEnumerable<byte[]>.GetEnumerator()
{
    Program.<GetBytes>d__0 d__;
    if (Interlocked.CompareExchange(ref this.<>1__state, 0, -2) == -2)
    {
        d__ = this;
    }
    else
    {
        d__ = new Program.<GetBytes>d__0(0);
    }
    d__.stream = this.<>3__stream;
    return d__;
}

Interlocked.CompareExchangeなんて使っています.ここはいったいどう読み取ればよいのでしょうか?
まずは文字面通りに考えます.this.<>1__stateと-2を比較する.もし-2と等しい場合は,this.<>1__stateに0を代入する.そのときInterlocked.CompareExchangeの戻り値はthis.<>1__stateの元の値なので-2になる.結果的にif文の条件式は真になるので,d__には自分自身(this)が設定される.
this.<>1__stateと-2の比較結果が等しくない場合は,this.<>1__stateには-2を設定しない.またInterlocked.CompareExchangeの戻り値も-2にはならない.だからif文の条件式は偽となり,d__にはd__0の新しいインスタンスが設定される.そのときコンストラクタの引数に渡される値は0である……
ずいぶんと回りくどいことをしていますが,きっと意味があるんでしょうね.とりあえず今の自分には分かりませんが.続きはまたいつか.(続けられるかな?)