C#foreach原理
本文主要记录我在学习C#中foreach遍历原理的心得体会。
对集合中的要素进行遍历是所有编码中经常涉及到的操作,因此大部分编程语言都把此过程写进了语法中,比如C#中的foreach。经常会看到下面的遍历代码:
- var lstStr = new List<string> { "a", "b" };
- foreach (var str in lstStr)
- {
- Console.WriteLine(str);
- }
实际此代码的执行过程:

- var lstStr = new List<string> {"a", "b"};
- IEnumerator<string> enumeratorLst = lstStr.GetEnumerator();
- while (enumeratorLst.MoveNext())
- {
- Console.WriteLine(enumeratorLst.Current);
- }

会发现有GetEnumerator()方法和IEnumerator<string>类型,这就涉及到可枚举类型和枚举器的概念。
为了方便理解,以下为非泛型示例:

- // 摘要:
- // 公开枚举器,该枚举器支持在非泛型集合上进行简单迭代。
- public interface IEnumerable
- {
- // 摘要:
- // 返回一个循环访问集合的枚举器。
- //
- // 返回结果:
- // 可用于循环访问集合的 System.Collections.IEnumerator 对象。
- IEnumerator GetEnumerator();
- }

实现了此接口的类称为可枚举类型,是可以用foreach进行遍历的标志。
方法GetEnumerator()的返回值是枚举器,可以理解为游标。

- // 摘要:
- // 支持对非泛型集合的简单迭代。
- public interface IEnumerator
- {
- // 摘要:
- // 获取集合中的当前元素。
- //
- // 返回结果:
- // 集合中的当前元素。
- //
- // 异常:
- // System.InvalidOperationException:
- // 枚举数定位在该集合的第一个元素之前或最后一个元素之后。
- object Current { get; }
- // 摘要:
- // 将枚举数推进到集合的下一个元素。
- //
- // 返回结果:
- // 如果枚举数成功地推进到下一个元素,则为 true;如果枚举数越过集合的结尾,则为 false。
- //
- // 异常:
- // System.InvalidOperationException:
- // 在创建了枚举数后集合被修改了。
- bool MoveNext();
- //
- // 摘要:
- // 将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。
- //
- // 异常:
- // System.InvalidOperationException:
- // 在创建了枚举数后集合被修改了。
- void Reset();
- }

而生成一个枚举器的的工具就是迭代器,以下是自定义一个迭代器的示例(https://msdn.microsoft.com/en-us/library/system.collections.ienumerator.aspx):

- using System;
- using System.Collections;
- // Simple business object.
- public class Person
- {
- public Person(string fName, string lName)
- {
- this.firstName = fName;
- this.lastName = lName;
- }
- public string firstName;
- public string lastName;
- }
- // Collection of Person objects. This class
- // implements IEnumerable so that it can be used
- // with ForEach syntax.
- public class People : IEnumerable
- {
- private Person[] _people;
- public People(Person[] pArray)
- {
- _people = new Person[pArray.Length];
- for (int i = ; i < pArray.Length; i++)
- {
- _people[i] = pArray[i];
- }
- }
- // Implementation for the GetEnumerator method.
- IEnumerator IEnumerable.GetEnumerator()
- {
- return (IEnumerator) GetEnumerator();
- }
- public PeopleEnum GetEnumerator()
- {
- return new PeopleEnum(_people);
- }
- }
- // When you implement IEnumerable, you must also implement IEnumerator.
- public class PeopleEnum : IEnumerator
- {
- public Person[] _people;
- // Enumerators are positioned before the first element
- // until the first MoveNext() call.
- int position = -;
- public PeopleEnum(Person[] list)
- {
- _people = list;
- }
- public bool MoveNext()
- {
- position++;
- return (position < _people.Length);
- }
- public void Reset()
- {
- position = -;
- }
- object IEnumerator.Current
- {
- get
- {
- return Current;
- }
- }
- public Person Current
- {
- get
- {
- try
- {
- return _people[position];
- }
- catch (IndexOutOfRangeException)
- {
- throw new InvalidOperationException();
- }
- }
- }
- }
- class App
- {
- static void Main()
- {
- Person[] peopleArray = new Person[]
- {
- new Person("John", "Smith"),
- new Person("Jim", "Johnson"),
- new Person("Sue", "Rabon"),
- };
- People peopleList = new People(peopleArray);
- foreach (Person p in peopleList)
- Console.WriteLine(p.firstName + " " + p.lastName);
- }
- }
- /* This code produces output similar to the following:
- *
- * John Smith
- * Jim Johnson
- * Sue Rabon
- *
- */

在有了yield这个关键字以后,我们可以通过这样的方式来创建枚举器:

- using System;
- using System.Collections;
- // Simple business object.
- public class Person
- {
- public Person(string fName, string lName)
- {
- this.firstName = fName;
- this.lastName = lName;
- }
- public string firstName;
- public string lastName;
- }
- // Collection of Person objects. This class
- // implements IEnumerable so that it can be used
- // with ForEach syntax.
- public class People : IEnumerable
- {
- private Person[] _people;
- public People(Person[] pArray)
- {
- _people = new Person[pArray.Length];
- for (int i = ; i < pArray.Length; i++)
- {
- _people[i] = pArray[i];
- }
- }
- // Implementation for the GetEnumerator method.
- IEnumerator IEnumerable.GetEnumerator()
- {
- for (int i = ; i < _people.Length; i++)
- {
- yield return _people[i];
- }
- }
- }
- class App
- {
- static void Main()
- {
- Person[] peopleArray = new Person[]
- {
- new Person("John", "Smith"),
- new Person("Jim", "Johnson"),
- new Person("Sue", "Rabon"),
- };
- People peopleList = new People(peopleArray);
- foreach (Person p in peopleList)
- Console.WriteLine(p.firstName + " " + p.lastName);
- }
- }

yield和return一起使用才有意义,这个关键字就是为迭代器服务的,所以有yield所在的方法是迭代器,作用是返回相同类型的值的一个序列,执行到yield return 这个语句之后,函数并不直接返回,而是保存此元素到序列中,继续执行,直到函数体结束或者yield break。
但是生成的此迭代器的函数体并不会在直接调用时运行,只会在foreach中迭代时执行。如:

- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Reflection;
- // Simple business object.
- public class Person
- {
- public Person(string fName, string lName)
- {
- this.firstName = fName;
- this.lastName = lName;
- }
- public string firstName;
- public string lastName;
- }
- // Collection of Person objects. This class
- // implements IEnumerable so that it can be used
- // with ForEach syntax.
- public class People
- {
- private Person[] _people;
- public People(Person[] pArray)
- {
- _people = new Person[pArray.Length];
- for (int i = ; i < pArray.Length; i++)
- {
- _people[i] = pArray[i];
- }
- }
- // Implementation for the GetEnumerator method.
- public IEnumerable GetMyEnumerator()
- {
- Console.WriteLine("a");
- for (int i = ; i < _people.Length; i++)
- {
- Console.WriteLine(i.ToString());
- yield return _people[i];
- }
- }
- }
- class App
- {
- static void Main()
- {
- Person[] peopleArray = new Person[]
- {
- new Person("John", "Smith"),
- new Person("Jim", "Johnson"),
- new Person("Sue", "Rabon"),
- };
- People peopleList = new People(peopleArray);
- IEnumerable myEnumerator = peopleList.GetMyEnumerator();
- Console.WriteLine("Start Iterate");
- foreach (Person p in myEnumerator)
- {
- Console.WriteLine(p.firstName + " " + p.lastName);
- }
- Console.ReadLine();
- }
- }

结果:
转载:https://www.cnblogs.com/alongdada/p/7598119.html
C#foreach原理的更多相关文章
- Foreach原理
本质:实现了一个IEnumerable接口, 01.为什么数组和集合可以使用foreach遍历? 解析:因为数组和集合都实现了IEnumerable接口,该接口中只有一个方法,GetEnumerato ...
- .net学习之集合、foreach原理、Hashtable、Path类、File类、Directory类、文件流FileStream类、压缩流GZipStream、拷贝大文件、序列化和反序列化
1.集合(1)ArrayList内部存储数据的是一个object数组,创建这个类的对象的时候,这个对象里的数组的长度为0(2)调用Add方法加元素的时候,如果第一次增加元神,就会将数组的长度变为4往里 ...
- Array.forEach原理,仿造一个类似功能
Array.forEach原理,仿造一个类似功能 array.forEach // 设一个arr数组 let arr = [12,45,78,165,68,124]; let sum = 0; // ...
- 浅析foreach原理
在日常开发工作中,我们发现很多对象都能通过foreach来遍历,比如HashTable.Dictionary.数组等数据类型.那为何这些对象能通过foreach来遍历呢?如果写一个普通的Person类 ...
- C#学习笔记:foreach原理
这篇随笔是对上一篇随笔C#关键字:yield的扩展. 关于foreach 首先,对于 foreach ,大家应该都非常熟悉,这里就简单的描述下. foreach 语句用于对实现 System.Col ...
- Foreach 原理
public class Person { private string[] friends = { "asf", "ewrqwe", "ddd&qu ...
- C# foreach 原理以及模拟的实现
public class Person:IEnumerable //定义一个person类 并且 实现IEnumerable 接口 (或者不用实现此接口 直接在类 //里面写个GetEnu ...
- 涉及 C#的 foreach问题
当时是用foreach实现遍历,但是函数传入参数是Object类型的,由于Objectl类型没有实现相关接口,所以foreach并不能执行. 那么下面我们来看看,想要使用foreach需要具备什么条件 ...
- mybatis foreach 循环 list(map)
直接上代码: 整体需求就是: 1.分页对象里面有map map里面又有数组对象 2.分页对象里面有list list里面有map map里面有数组对象. public class Page { pri ...
随机推荐
- 原生js实现图片瀑布流布局,注释超详细
完整代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...
- js实现初始化调用摄像头
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"% ...
- 透过源码看懂Flink核心框架的执行流程
前言 Flink是大数据处理领域最近很火的一个开源的分布式.高性能的流式处理框架,其对数据的处理可以达到毫秒级别.本文以一个来自官网的WordCount例子为引,全面阐述flink的核心架构及执行流程 ...
- CentOS 7 Nacos 集群搭建
环境 CentOS 7.4 MySQL 5.7 nacos-server-1.1.2 本次安装的软件全部在 /home/javateam 目录下. MySQL 安装 首先下载 rpm 安装包,地址:h ...
- MongoDB入门三
MongoDB字段问题 增删查改操作 删除一列操作db.RiderReaTimePositon.update({},{$unset:{'CreateTime':''}},false,true)db. ...
- js基础练习题(6)
10.其他 1.选择题 var name = 'World!'; (function () { if (typeof name === 'undefined') { var name = 'Nodei ...
- js事件入门(1)
1.事件相关概念 1.1 什么是事件? 事件是用户在访问页面时执行的操作,也就是用户访问页面时的行为.当浏览器探测到一个事件时,比如鼠标点击或者按键.它可以触发与这个事件相关的JavaScript对象 ...
- Java中时间加减的比较
public class TestDate{ public static void main(String[] args){try{ Date date=new Date(); DateFormat ...
- API(List、Map)
day 07 API List接口 特点: 有序,带索引,内容可以重复 Arraylist: 创建对象一般使用多态的格式: List<E> li = new ArrayList<E& ...
- 循环&&数组&&方法&&面向对象
day03 数值的默认值 类型 初始化的值 byte,short,int,long 0 float,double 0.0 char 空格 boolean false 引用类型 null JVM的内存 ...