C#从foreach语句中枚举元素看数组详解
前言
在foreach语句中使用枚举,可以迭代数组或集合中的元素,且无须知道集合中的元素的个数。如图显示了调用foreach方法的客户端和集合之间的关系。数组或集合实现带getenumerator()
方法的ienumerable接口。getenumerator()
方法返回一个实现lenumerable接口的枚举,接着foreach语句就可以使用ienumerable接口迭代集合了。
getenumerator()
方法用ienumerable接口定义,foreach语句并不真的需要在集合类中实现这个接口。有一个名为getenumerator()
的方法它返回实现了ienumerator接口的对象就足够了。
先定义一个person类,这个类有自动实现的属性firstname和lastname,以及从object类重写tostring方法和继承泛型接口iequatable以比较两个对象是否相等,实现泛型接口icomparer以比较两个对象用来排序。
public class person : iequatable<person>,icomparable<person> { public int id { get; private set; } public string firstname { get; set; } public string lastname { get; set; } public override string tostring() { return string.format("{0}, {1} {2}", id, firstname, lastname); } public bool equals(person other) { if (other == null) return base.equals(other); return this.firstname == other.firstname && this.lastname == other.lastname; } public int compareto(person other) { if (other == null) throw new argumentnullexception("other"); int result = this.lastname.compareto(other.lastname); if (result == 0) { result = this.firstname.compareto(other.firstname); } return result; } }
创建一个三个元素的person数组,现对数组进行排序在用foreach循环访问数组中的元素并输出
person[] persons = { new person { firstname = "simen03", lastname = "go" }, new person { firstname = "simen02", lastname = "go" }, new person { firstname = "simen01", lastname = "go" } }; array.sort(persons); foreach (var person in persons) console.writeline(person);
分析foreach (var person in persons)console.writeline(person);
这段代码il代码
// loop start (head: il_009b) il_008a: ldloc.2 il_008b: ldloc.3 il_008c: ldelem.ref il_008d: stloc.s person il_008f: ldloc.s person il_0091: call void [mscorlib]system.console::writeline(object) il_0096: nop il_0097: ldloc.3 il_0098: ldc.i4.1 il_0099: add il_009a: stloc.3 il_009b: ldloc.3 il_009c: ldloc.2 il_009d: ldlen il_009e: conv.i4 il_009f: blt.s il_008a // end loop
c#的foreach语句不会解析为il代码中的foreach语句,c#编译器会把foreach语句转换为ienumerable接口的方法和属性,foreach语句使用ienumerator接口的方法和属性,迭代数组中的所有元素,为此,ienumerator定义了current属性,来返回光标所在的元素,该接口的movenext()
方法移动到数组的下一个元素上,如果有这个元素该方法就返回true否则返回false,这个接口的泛型版本ienumerator派生自接口idisposable,因此定义了dispose()
方法来清理枚举器占用的资源,使用foreach语句会解析为下面的代码段
ienumerator enumerator = persons.getenumerator(); while (enumerator.movenext()) { var person = enumerator.current; console.writeline(person); }
为了方便的创建枚举器,c#添加了yield语句,yield return 语句返回集合的一个元素,并移动到下一个元素,yield break 可停止迭代。使用迭代块,编译器会生成一个yield类型,其中包含一个状态机,如下代码段所示。yield 类型实现ienumerator和idisposable接口的属性和方法。在下面的例子中,可以把yield类型看作内部类enumerator.外部类的getenumerator()
方法实例化并返回一个新的yield类型。在yield类型中,变量state定义了迭代的当前位置,每次调用movenext()
时,当前位置都会改变,movenext()
封装了迭代代码,并设置了current变量的值,从而使current属性根据位置返回一个对象。
static void main(string[] args) { var hellocollection = new hellocollection(); foreach (string s in hellocollection) { console.writeline(s); } } public class hellocollection { public ienumerator<string> getenumerator() { yield return "hello"; yield return "world"; } } public class hellocollectionother { public ienumerator getenumertor() { return new enumerator(0); } public class enumerator : ienumerator<string>, ienumerator, idisposable { private int state; private string current; public enumerator(int state) { this.state = state; } public string current => throw new notimplementedexception(); object ienumerator.current { get { return current; } } public void dispose() { throw new notimplementedexception(); } public bool movenext() { switch (state) { case 0:current = "hello"; state = 1; return true; case 1:current = "world"; state = 2; return true; case 2: break; } return false; } public void reset() { throw new notimplementedexception(); } } }
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对的支持。
上一篇: 孕妇也可适当运动 练习瑜伽有好处