在日常开发工作中,我们发现很多对象都能通过foreach来遍历,比如HashTable、Dictionary、数组等数据类型。那为何这些对象能通过foreach来遍历呢?如果写一个普通的Person类,也希望它能通过foreach来遍历应该怎么做?通过查看,发现HashTable、Dictionary、数组等数据类型都实现了一个叫IEnumerable(或其泛型版本)的接口。现在也来尝试下,让Person类实现这个接口(其实实不实现IEnumerable接口不是必须的,只要类型中有public IEnumerator GetEnumerator()这个方法即可):

 1 class Person:IEnumerable
 2     {
 3         public string[] _Name = new string[] { "sk", "jk", "yzk","wcw","ml" };
 4         public string Name { get; set; }
 5         public int Age { get; set; }
 6
 7         public IEnumerator GetEnumerator()
 8         {
 9             return new PersonEnumerator(_Name);
10         }
11     }

可以看到GetEnumerator()方法需要一个返回值类型为实现了IEnumerator的类型,那就写个类,让其实现IEnumerator接口:

 1 class PersonEnumerator:IEnumerator
 2     {
 3         public PersonEnumerator(string[] name)
 4         {
 5             this._names = name;
 6         }
 7
 8         public string[] _names { get; set; }
 9         public int Index = -1;
10
11         public object Current
12         {
13             get { return _names[Index]; }
14         }
15
16         public bool MoveNext()
17         {
18             Index++;
19             if (Index>=_names.Length)
20             {
21                 return false;
22             }
23             else
24             {
25                 return true;
26             }
27         }
28
29         public void Reset()
30         {
31             Index = -1;
32         }
33     }

此时再运行程序,就发现Person类可以遍历了。运行结果如下:

总结,一种类型要想通过foreach遍历,其内部必须有public IEnumerator GetEnumerator()这个方法,而通常的做法是让类型实现IEnumerable接口。

上面的代码还有一个问题,就是Person对象通过foreach遍历时,var并没有推断出是string类型而是object类型,这是因为Current就是object导致的,解决的方案就是泛型,看下面的代码:

 1 class Person
 2     {
 3         public string Name { get; set; }
 4         public int Age { get; set; }
 5         public string[] _Name = new string[] { "zxh", "jk", "ml", "wcw", "sk", "yzk","lmn" };
 6
 7         public IEnumerator<string> GetEnumerator()
 8         {
 9             return new MyClass<string>(_Name);
10         }
11     }
12
13     class MyClass<T>:IEnumerator<T>
14     {
15         public MyClass(T[] _Names)
16         {
17             this.names = _Names;
18         }
19         public T Current
20         {
21             get { return names[Index]; }
22         }
23
24         private T[] names { get; set; }
25         private int Index = -1;
26
27         public void Dispose()
28         {
29             //throw new NotImplementedException();
30         }
31
32         object IEnumerator.Current
33         {
34             get { throw new NotImplementedException(); }
35         }
36
37         public bool MoveNext()
38         {
39             Index++;
40             if (Index>=names.Length)
41             {
42                 return false;
43             }
44             else
45             {
46                 return true;
47             }
48         }
49
50         public void Reset()
51         {
52             Index = -1;
53         }
54     }

使用的方法如下:

1            Person p1 = new Person();
2             foreach (var item in p1)
3             {
4                 Console.WriteLine(item);
5             }

可以看到var已经被推断成string.

下面看一下,自己写的foreach遍历,还是那个Person类(必须有IEnumerator GetEnumerator()这个方法):

把foreach遍历换成下面的代码:

1            Person p1 = new Person();
2             IEnumerator enumer = p1.GetEnumerator();
3             while (enumer.MoveNext())
4             {
5                 Console.WriteLine(enumer.Current);
6             }

上面的代码也是能够正常执行的。

扩展:

1             List<string> strLst = new List<string>();   //查看定义得知,其实现了IEnumerable<T>和IEnumerable2个接口
2             strLst.AddRange(new string[] { "sk", "jk", "yzk" });
3             IEnumerator enumer = strLst.GetEnumerator();
4             while (enumer.MoveNext())
5             {
6                 Console.WriteLine(enumer.Current);
7             }

一般没人会这么写,遍历一个对象还是直接写foreach。这只不过是其原理。

 1             Dictionary<int, char> dic = new Dictionary<int, char>();
 2             dic.Add(1, '壹');
 3             dic.Add(2, '贰');
 4             dic.Add(3, '叁');
 5             dic.Add(4, '肆');
 6             dic.Add(5, '伍');
 7             dic.Add(6, '陆');
 8             dic.Add(7,'柒');
 9             dic.Add(8,'扒');
10             dic.Add(9,'玖');
11
12             IEnumerator iEnumer = dic.GetEnumerator();
13             while (iEnumer.MoveNext())
14             {
15                 KeyValuePair<int, char> item = (KeyValuePair<int, char>)iEnumer.Current;
16                 Console.WriteLine(item.Key+"   "+item.Value);
17             }

转载于:https://www.cnblogs.com/chens2865/p/3860619.html

浅析foreach原理相关推荐

  1. 浅析bootstrap原理及优缺点

    网格系统的实现原理,是通过定义容器大小,平分12份(也有平分成24份或32份,但12份是最常见的),再调整内外边距,最后结合媒体查询,就制作出了强大的响应式网格系统 网格系统的实现原理,是通过定义容器 ...

  2. 手把手带你学会Odoo OWL组件开发(5):浅析OWL原理

    [本系列内容直达:] 手把手带你学习Odoo OWL组件开发(1):认识 OWL 手把手带你学会Odoo OWL组件开发(2):OWL的使用 手把手带你学会Odoo OWL组件开发(3):核心内容指南 ...

  3. java foreach 原理_一不小心就让Java开发者踩坑的failfast是个什么鬼?

    1 什么是fail-fast 首先我们看下维基百科中关于fail-fast的解释: 在系统设计中,快速失效系统一种可以立即报告任何可能表明故障的情况的系统.快速失效系统通常设计用于停止正常操作,而不是 ...

  4. 浅析Kerberos原理,及其应用和管理

    文章作者:luxianghao 文章来源:http://www.cnblogs.com/luxianghao/p/5269739.html  转载请注明,谢谢合作. 免责声明:文章内容仅代表个人观点, ...

  5. 浅析ajax原理与用法

    1 ajax原理 Ajax(Asynchronous JavaScript and XML (异步的JavaScript和XML)),是一种快速创建 动态网页的技术,目的是显示动态局部刷新.通过XML ...

  6. TUN/TAP设备浅析(一) -- 原理浅析

    TUN/TAP设备浅析 TUN设备 TUN 设备是一种虚拟网络设备,通过此设备,程序可以方便地模拟网络行为.TUN 模拟的是一个三层设备,也就是说,通过它可以处理来自网络层的数据,更通俗一点的说,通过 ...

  7. 浅析ElasticSearch原理

    女主宣言 最近女主在项目中使用到ElasticSearch来做索引.但是对ElasticSearch的一些原理还是比较模糊,所以就梳理了一下ElasticSearch的基本原理,分享给大家. PS:丰 ...

  8. 浅析SSRF原理及利用方式

    原文链接:https://www.anquanke.com/post/id/145519 漏洞简介 SSRF(Server-side Request Forge, 服务端请求伪造) 通常用于控制web ...

  9. 浅析Promise原理

    Promise原型对象 在浏览器控制台输入如下代码,可以看到Promise原型对象信息. var p = new Promise(()=>{}); console.log(p) Promise原 ...

最新文章

  1. WPF加载相对路径的图片的解决方法
  2. Spring 注解之@RestController与@Controller的区别
  3. 搜狗手机助手联合腾讯御安全 共建APP安全生态环境
  4. 10_史上最全的Markdown使用教程(没有之一)(20190115)
  5. ThreadLocal 内存泄露的实例分析
  6. Ubuntu 首次给root用户设置密码
  7. U(优)盘安装FreeBSD-9.0+GNOME_lite桌面
  8. vue实现的tabs标签组件
  9. linux消息队列的内核限制
  10. Java新特性之Nashorn的实例详解
  11. webrtc java api_java – 使用WebSockets实现WebRTC信令
  12. webstorm 高效开发 (html)
  13. 技巧8——linux假死现象要知道
  14. 怎么利用企业微信营销 企业微信如何营销 企业微信如何维护好友 企业微信如何开通
  15. 工具说明书 - FTDI芯片的USB转UART串口线
  16. 为什么您没有得到最好的承包商,您将如何做(第2部分)
  17. 如何选择字体(font-family)
  18. 康沣生物通过港交所聆讯:持续亏损、现金流紧张,李克俭为董事长
  19. 哈夫曼树的构建及哈夫曼树编码
  20. Unity特效学习笔记——子弹

热门文章

  1. freemarker如何判空容错
  2. sql 纵向求和_sql 行列转换 求和平均值等
  3. 两个字符串的最长公共子序列长度_算法学习笔记(58): 最长公共子序列
  4. 无线短距通信技术标准:WIFI,蓝牙,ZigBee
  5. c语言编译asn1文件,使用 asn1.c 开源编译工具生成 S1AP R15消息编解码C文件
  6. electronjs设置宽度_electronjs 入门_2019年2月14日
  7. SQL Server高级查询之子查询(子查询非典型应用)
  8. SSM之Mybatis框架
  9. [leetcode]241. 为运算表达式设计优先级
  10. bzoj 3396: [Usaco2009 Jan]Total flow 水流(最大流)