//
我以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;
}
}
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;
}
}
}
//
使用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);
}
}
而用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的文章大家有兴趣可以查一下,具体网址我不记得了不好意思).
1126





