博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#中foreach实现原理
阅读量:5047 次
发布时间:2019-06-12

本文共 7021 字,大约阅读时间需要 23 分钟。

本文主要记录我在学习C#中foreach遍历原理的心得体会。

 

对集合中的要素进行遍历是所有编码中经常涉及到的操作,因此大部分编程语言都把此过程写进了语法中,比如C#中的foreach。经常会看到下面的遍历代码:

var lstStr = new List
{ "a", "b" }; foreach (var str in lstStr) { Console.WriteLine(str); }

实际此代码的执行过程:

var lstStr = new List
{
"a", "b"}; IEnumerator
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 = 0; 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 = -1;    public PeopleEnum(Person[] list)    {        _people = list;    }    public bool MoveNext()    {        position++;        return (position < _people.Length);    }    public void Reset()    {        position = -1;    }    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[3]        {            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 = 0; i < pArray.Length; i++)        {            _people[i] = pArray[i];        }    }    // Implementation for the GetEnumerator method.    IEnumerator IEnumerable.GetEnumerator()    {        for (int i = 0; i < _people.Length; i++)        {            yield return _people[i];        }    }}class App{    static void Main()    {        Person[] peopleArray = new Person[3]        {            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 = 0; i < pArray.Length; i++)        {            _people[i] = pArray[i];        }    }    // Implementation for the GetEnumerator method.    public IEnumerable GetMyEnumerator()    {        Console.WriteLine("a");        for (int i = 0; i < _people.Length; i++)        {            Console.WriteLine(i.ToString());            yield return _people[i];        }    }}class App{    static void Main()    {        Person[] peopleArray = new Person[3]        {            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();    }}

结果:

 

  

 

 

 

 

 

 

 

 

 

 

 

 

参考资料:http://www.cnblogs.com/zouzf/archive/2012/02/22/2362954.html

转载于:https://www.cnblogs.com/alongdada/p/7598119.html

你可能感兴趣的文章
RabbitMQ 消息顺序、消息幂等、消息重复、消息事务、集群
查看>>
k64 datasheet学习笔记1---概述
查看>>
LeetCode 121 Best Time to Buy and Sell Stock
查看>>
Lambda 表达式的示例
查看>>
linux文件系统初始化过程(1)---概述
查看>>
NIO
查看>>
FASTREPORT 整理 (mtm)
查看>>
用设计精美的阅读指读应用项目源码
查看>>
Hhml 標題 閃爍
查看>>
调用系统计算器n次
查看>>
shell study
查看>>
2017-2018-1 20155220 《信息安全系统设计基础》第五周学习总结
查看>>
并发通信、生产者消费者模型
查看>>
IOS开发创建开发证书及发布App应用(二)——创建证书
查看>>
伪随机数生成方法
查看>>
windows下使用 ApiGen 生成php项目的开发文档
查看>>
好用的Android屏幕适配
查看>>
java的System.getProperty()方法可以获取的值
查看>>
mysql 安装
查看>>
JS 和 Jq 获取客户端各种屏幕宽度和高度
查看>>