电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

foreach和yield


发布日期:2019/9/24
 

foreach

foreach无需要知道集合中元素个数就可以迭代集合中的元素它其实是迭代器模式的一个包装就语言层面来说是while的另一种形式

using System;

using SystemCollections;

public class People : IEnumerable//版本{ string[] names; public People(string[] names) { thisnames = names; } public IEnumerator GetEnumerator() { return new Enumerator(thisnames); } private class Enumerator : IEnumerator { private int state = ; string[] names; internal Enumerator(string[] names) { thisnames = names; } public bool MoveNext() { state++; return (state < namesLength); } public void Reset() { state = ; } public object Current { get { try { return names[state]; } catch (IndexOutOfRangeException) { throw new InvalidOperationException(); } } } }}public class Test{ public static void Main() { People people = new People(new string[]{aaabbbcccddd}); foreach (var person in people) { ConsoleWriteLine(person); } IEnumerator Enumerator = peopleGetEnumerator(); while (EnumeratorMoveNext()) { ConsoleWriteLine(EnumeratorCurrent); } }}查看IL代码先看foreach代码块的IL代码

IL_f: callvirt instance class [mscorlib]SystemCollectionsIEnumerator People::GetEnumerator()

IL_: stlocs CS$$

try

{

IL_: brs IL_

IL_: ldlocs CS$$

IL_a: callvirt instance object [mscorlib]SystemCollectionsIEnumerator::get_Current()

IL_f: stloc

IL_: ldloc

IL_: call void [mscorlib]SystemConsole::WriteLine(object)

IL_: ldlocs CS$$

IL_: callvirt instance bool [mscorlib]SystemCollectionsIEnumerator::MoveNext()

IL_d: brtrues IL_

IL_f: leaves IL_

} // end try

finally

{

IL_: ldlocs CS$$

IL_: isinst [mscorlib]SystemIDisposable

IL_: stlocs CS$$

IL_a: ldlocs CS$$

IL_c: brfalses IL_

IL_e: ldlocs CS$$

IL_: callvirt instance void [mscorlib]SystemIDisposable::Dispose()

IL_: endfinally

} // end handler再看while代码块的IL代码

IL_: callvirt instance class [mscorlib]SystemCollectionsIEnumerator People::GetEnumerator()

IL_: stloc

IL_: brs IL_

IL_: ldloc

IL_a: callvirt instance object [mscorlib]SystemCollectionsIEnumerator::get_Current()

IL_f: call void [mscorlib]SystemConsole::WriteLine(object)

IL_: ldloc

IL_: callvirt instance bool [mscorlib]SystemCollectionsIEnumerator::MoveNext()

IL_a: brtrues IL_可以看出foreach语句块IL代码与while语句块的IL代码相比只多了finally部分的资源处理但循环部分的代码基本没有任何分别

如果让private class Enumerator 增加IDisposable接口将while语句块改写为如下

using((IDisposable)Enumerator)

{

while (EnumeratorMoveNext())

{

ConsoleWriteLine(EnumeratorCurrent);

}

}得到的IL代码将与foreach基本没有区别

由此可见foreach是编译器给我们的一个语法糖它包装了while

yield

在上面的代码中枚举器private class Enumerator 是自己实现了IEnumerator接口中的方法使用yield语句可以让编译器自动实现省去了自己写的麻烦(包含yield语句的方法或属性必须声明为返回IEnumerable 或IEnumerator )所以上面的public class People 可以这样写

public class People : IEnumerable//版本

{

string[] names;

public People(string[] names)

{

thisnames = names;

}

public IEnumerator GetEnumerator()

{

for (int i = ; i < namesLength; i++)

{

yield return names[i];

}

}

}

反编译之后看IL代码

这是版本people类

这是版本的people类

自己写的迭代器类Enumerator和编译器生成的迭代器类<GetEnumerator>d_相比较<GetEnumerator>d_增加了IDisposable接口另外就是增加了IEnumerator接口的泛型版本的继承如果愿意Enumerator也可以实现这两个接口

用yield还可以返回IEnumerable接口类型

using System;

using SystemCollections;

public class People

{

string[] names;

public People(string[] names)

{

thisnames = names;

}

public IEnumerable PeopleEven()

{

for (int i = ; i < namesLength; i += )

{

yield return names[i];

}

}

}

public class Test

{

public static void Main()

{

People people = new People(new string[] { aaa bbb ccc ddd });

foreach (var person in peoplePeopleEven())

{

ConsoleWriteLine(person);

}

}

}

该PeopleEven()返回的IEnumerable类<PeopleEven>d_会自动实现GetEnumerator()方法和IEnumerator接口中的方法看IL代码

PeopleEven()返回的类<PeopleEven>d_实现了IEnumerable和IEnumerator接口以及它们的泛型版本还实现了IDisposable接口因此可以将这个接口都实现将类改写

using System;

using SystemCollections;

public class People : IEnumerable IEnumerator IDisposable

{

string[] names = { bbbb dddd };

int state = ;

public IEnumerator GetEnumerator()

{

return new People();

}

public bool MoveNext()

{

state++;

return (state < namesLength);

}

public void Reset()

{

state = ;

}

public object Current

{

get

{

try

{

return names[state];

}

catch (IndexOutOfRangeException)

{

throw new InvalidOperationException();

}

}

}

public void Dispose()

{

}

}

public class Test

{

public static void Main()

{

People people = new People();

foreach (var person in people)

{

ConsoleWriteLine(person);

}

}

}

看IL代码

类People与PeopleEven()返回的类<PeopleEven>d_基本相同只是少了两个接口的泛型版本的实现

由此可见yield是一个更大的语法糖它可以自动实现IEnumerable和IEnumberator接口中的方法自动替我们实现迭代器

用foreach和yield可以非常方便地使用迭代模式

上一篇:使用ZedGraph记录

下一篇:解决图片被盗链接的问题?