迭代器设计模式举例:
namespace YieldSample01
{
public class HelloCollection: IEnumerable
{
public IEnumerator GetEnumerator()
{
returnnew Enumerator(-1);
}
public class Enumerator :IEnumerator
{
privateint _state;
publicobject Current { get;private set; }
publicEnumerator(int state)
{
_state = state;
}
publicbool MoveNext()
{
_state++;
switch(_state)
{
case0:
Current = "Hello";
returntrue;
case1:
Current = "World";
return true;
default:
return false;
}
}
publicvoid Reset()
{
_state = -1;
}
}
}
}
namespace YieldSample01
{
class Program
{
static void Main(string[]args)
{
HelloCollectionhelloCollection = new HelloCollection();
foreach(string s inhelloCollection)
{
Console.WriteLine(s);
}
}
}
}
可枚举类型和可枚举数在.NET集合类中广泛使用,所以知道它们如何工作很重要。但是,既然我们已经知道如何创建自己的可枚举类以及枚举数了,我们可能会很高兴听到,C#从2.0版本开始提供了更简单的创建枚举数和可枚举类型的方式。其实,编译器会为我们创建它们。这种结构叫做迭代器。我们可以把手动编码的可枚举类型和枚举数替换为由迭代器生成的可枚举类型和枚举数。
C#迭代器举例:
namespace YieldSample02
{
public class HelloCollection:IEnumerable
{
public IEnumerator GetEnumerator()
{
yieldreturn "Hello";
yieldreturn "World";
}
}
}
namespace YieldSample02
{
class Program
{
static void Main(string[]args)
{
HelloCollectionhelloCollection = new HelloCollection();
foreach(string s inhelloCollection)
{
Console.WriteLine(s);
}
}
}
}
【迭代器块】
迭代器块是一个或多个yield语句的代码块。
迭代器块被认为与其他代码块不同。其他块包含的语句被当作是命令式的。也就是说,代码块的第一个语句被执行然后是后面的语句,最后控制离开块。
然而,迭代器块不是需要在同一时间执行的一序列命令式命令,而是描述了希望编译器为我们创建的枚举数的行为(编译器生成一个类来实现迭代器块中表示的行为)。迭代器块中的代码描述了如何枚举元素。
迭代器块有两个特殊语句:
yield return <expression> 语句返回集合的一个元素,并移动到下一个元素上。
yield break 语句停止迭代。
例子一:
namespace YieldSample03
{
publicclass A : IEnumerable<int>
{
privatereadonly int[]_array = new int[10];
publicA()
{
for(int i = 0; i < _array.Length; i++)
{
_array[i] = i;
}
}
publicIEnumerator<int>GetEnumerator()
{
for(int i = 0; i < _array.Length; i++)
{
yield return_array[i];
}
}
IEnumeratorIEnumerable.GetEnumerator()
{
returnGetEnumerator();
}
}
}
namespace YieldSample03
{
classProgram
{
staticvoid Main(string[]args)
{
Aa = new A();
foreach(int i in a)
{
Console.WriteLine(i);
}
}
}
}
例子二:
namespace YieldSample04
{
publicclass A : IEnumerable<int>
{
privatereadonly int[]_array = new int[10];
publicA()
{
for(int i = 0; i < _array.Length; i++)
{
_array[i] = i;
}
}
publicIEnumerator<int>GetEnumerator()
{
for(int i = 0; i < _array.Length; i++)
{
if (i < 8)
{
yield return_array[i];
}
else
{
yield break;
}
}
}
IEnumeratorIEnumerable.GetEnumerator()
{
returnGetEnumerator();
}
}
}
namespace YieldSample04
{
classProgram
{
staticvoid Main(string[]args)
{
Aa = new A();
foreach(int i in a)
{
Console.WriteLine(i);
}
}
}
}
例子三:
namespace YieldSample05
{
public class MyClass:IEnumerable<string>
{
public IEnumerator<string>GetEnumerator()
{
yieldreturn "black";
yieldreturn "gray";
yieldreturn "white";
}
IEnumeratorIEnumerable.GetEnumerator()
{
returnGetEnumerator();
}
}
}
namespace YieldSample05
{
class Program
{
static void Main(string[]args)
{
MyClassmc = new MyClass();
foreach(string shade inmc)
{
Console.WriteLine(shade);
}
}
}
}
例子四:
namespace YieldSample06
{
public class MyClass:IEnumerable<string>
{
privatereadonly bool_colorFlag = true;
publicMyClass(bool flag)
{
_colorFlag = flag;
}
privateIEnumerator<string>BalckAndWhite
{
get
{
yieldreturn "black";
yieldreturn "gray";
yieldreturn "white";
}
}
privateIEnumerator<string>Colors
{
get
{
string[]theColors = {"blue","red","yellow"};
for(int i = 0; i < theColors.Length; i++)
yieldreturn theColors[i];
}
}
public IEnumerator<string>GetEnumerator()
{
return_colorFlag ? BalckAndWhite : Colors;
}
IEnumeratorIEnumerable.GetEnumerator()
{
returnGetEnumerator();
}
}
}
namespace YieldSample06
{
internal class Program
{
privatestatic voidMain(string[] args)
{
MyClassmc1 = new MyClass(true);
foreach(string s inmc1)
{
Console.WriteLine(s);
}
Console.WriteLine("--------");
MyClassmc2 = new MyClass(false);
foreach(string s inmc2)
{
Console.WriteLine(s);
}
Console.WriteLine("--------");
IEnumerator<string> enu = mc2.GetEnumerator();
//enu.Reset();//抛异常
while(enu.MoveNext())
{
Console.WriteLine(enu.Current);
}
}
}
}
【迭代器实质】
迭代器需要System.Collection.Generic命名空间,因此我们需要使用using指令包含它。
在编译器生成的枚举数中,Reset方法没有实现。而它是接口需要的方法,因此调用实现时总是抛出System.NotSupportedException异常。
在后台,由编译器生成的枚举数是有4个状态的状态机。
Before:首次调用MoveNext的初始状态。
Running:调用MoveNext后进入这个状态。在这个状态中,枚举数检测并设置下一项的位置。在遇到yield return、yieldbreak或在迭代器体结束时,退出状态。
Suspended:状态机等待下一次调用MoveNext的状态。
After:没有更多可以枚举。
如果状态机在Before或Suspended状态,并且有一次MoveNext方法调用,它就转到了Running状态。在Running状态中,它检测集合的下一项并设置位置。
如果有更多项,状态机会转入Suspended状态,如果没有更多项,它转入并保持在After状态。
通过微信学习的知识只能是碎片化的知识,作为新时代的我们希望能够构建自己的知识结构,使我们的知识体系化,系统化,以后在遇到碎片化的知识,我们做的只是融合到自己的知识结构中,故我们将推出“与LSGO一起学”系列课程,帮助大家来构建知识框架,初步规划有:
“与LSGO一起学C++”;
“与LSGO一起学C#”;
“与LSGO一起学Matlab”;
“与LSGO一起学数据结构”;
“与LSGO一起学设计模式”;
“与LSGO一起学可视化建模语言(UML)”;
“与LSGO一起学线性代数”;
“与LSGO一起学高等数学”
“与LSGO一起学概率论与数理统计”;
“与LSGO一起学抽象代数;
“与LSGO一起学点集拓扑”
“与LSGO一起学数字图像处理”;
“与LSGO一起学智能计算”;
如果对这些内容感兴趣,可以一起来学习讨论。
我们的官网: www.lsgogroup.com
联系客服