yield是c#2.0中引入的關鍵詞,以前只是匆匆看過它的用法,覺得不過是個語法糖.這兩天突然對yIEld產生了興趣進行了一些小實驗.有了一些心得放在這裡與大家共享.
yIEld確實是一塊非常好的語法糖,方便我們寫iterator.比如我要給Tuple加一個迭代功能就非常容易.

//我以Tuple<T1, T2, T3>舉例

public class Tuple<T1, T2, T3> : Tuple<T1, T2>,IEnumerable


...{

T3 _t3;

public Tuple(T1 t1, T2 t2, T3 t3) : base(t1, t2)


...{

this._t3 = t3;

}

//很方便的加入迭代功能

public override IEnumerator GetEnumerator()


...{

yIEld return this.Item_1;

yIEld return this.Item_2;

yIEld return this._t3;

}

}
yIEld return相當於執行了一次MoveNext(); 並返回Current的過程.所以在上面的代碼裡我才能非常容易的給Tuple加上迭代功能.
yIEld不僅能夠簡化實現迭代功能,它還能幫助我們換種方式實現遞歸.來看看下面這個演示函數它非常容易的實現了一個中序遍歷

IEnumerable<T> ScanInOrder(Node<T> root)


...{

if(root.LeftNode != null)


...{

foreach(T item in ScanInOrder(root.LeftNode))


...{

yIEld return item;

}

}

yIEld return root.Item;

if(root.RightNode != null)


...{

foreach(T item in ScanInOrder(root.RightNode))


...{

yIEld return item;

}

}

}
而在我前面的文章中介紹了用尾遞歸的方式計算Fibonacci數列的方法.在這裡我要用yIEld實現一個不同版本.

//使用YIEld計算Fibonacci數列

public long Fib_YIEld(int n)


...{

long num = 0;

int index = 3;

foreach (long b in this.YIEld(1, 1))


...{

if (index == n)


...{

num = b;

break;

}

else

++index;

}

return num;

}

public IEnumerable<long> YIEld(long b1, long b2)
...{

yIEld return b1 + b2;

foreach (long b in YIEld(b2, b1 + b2))

yIEld return b;

}
這種實現方式和遞歸函數不同的地方就是它沒有終止條件.上面的Yield函數實際上能夠計算一個無限數列(這有點像FP中的惰性計算)而Fib_YIEld函數才是真正實現業務邏輯的地方.這是不是有點業務邏輯和通用算法分離的感覺.再讓我們看一個例子.
在寫WebForm或WinForm程序的時候我們經常會應用一個技巧就是把業務實體對象(Entity Object)的數據賦值給UI上的控件.所以我們就會寫一個遞歸遍歷所有控件的函數類似於如下函數:

public void SetValue(Control ctl, EntityObject obj)


...{

foreach(Control c in ctl.Controls)


...{

if(c is TextBox)


...{

//利用反射將obj的數據賦值給該控件

}

if(c is CheckBox)


...{

//利用反射將obj的數據賦值給該控件

}

//......

if(c.HasControls())

SetValue(c, obj);

}

}
//GetValue函數與此相仿
而用yIEld就會變成這個樣子(注:關於Tuple的HasType()函數請看這裡)

public IEnumerable<Control> Iterator(Control baseCtl, Tuple t)


...{

foreach(Control c in baseCtl.Controls)


...{

if (!t.HasType(c))


...{

foreach (Control c1 in Iterator(c, t))

yIEld return c1;

}

yIEld return c;

}

}

public void SetValue(EntityObject obj)


...{

foreach(Control c in Iterator(this, new Tuple<TextBox, CheckBox, Button>()))


...{

//操作c

}

}
這樣我就可以把Iterator函數放到基礎庫中,而把對WebForm和WinForm控件的賦值操作放到業務邏輯層中.實現了一定的解耦.
看來yield還有待挖掘的潛力,特別是它可以把函數中的臨時變量保存到棧上的能力,我覺得可以在多線程編程中應用yield(微軟msdn雜志有一篇介紹應用yIEld實現異步IO的文章大家有興趣可以查一下,具體網址我不記得了不好意思).