C# 通过IEnumberable接口和IEnumerator接口实现自定义集合类型foreach功能
1、IEnumerator和IEnumerable的作用
其实IEnumerator和IEnumerable的作用很简单,就是让除数组和集合之外的类型也能支持foreach循环,至于foreach循环,如果不清楚,请参考C# foreach循环较for循环的优势与劣势
代码如下:
static void Main(string[] args)
{
CatList cats = new CatList();
foreach (var cat in cats)
{
Console.WriteLine(cat.NickName);
}
}
public class CatList
{
public string NickName { get; set; }
public int Age { get; set; }
}
以上代码说明自定义集合类型(假设CatList是集合类型)是无法使用foreach进行循环的.
原因是C#中自定义集合类型要实现foreach的功能,必须通过IEnumerator和IEnumerable两个接口来实现!
2、通过IEnumerator和IEnumerable两个接口实现自定义集合类型的foreach循环功能.
第一步:实现自定义集合类型实现IEnumerable接口,实现该接口的字面意思可以理解为:自定义集合类型实现了该接口,就拥有了"可枚举的功能".代码如下:
实现了IEnumerable接口之后,发现该接口规定必须返回一个IEnumerator接口(迭代器对象).ok,那么就必须返回一个IEnumerator,那么它是什么呢?别急,下面开始介绍这个接口!
第二步:通过IEnumerable要求实现的GetEnumerator()方法返回一个IEnumerator(迭代器对象),实现该接口必须实现以下三个方法/属性:
(1)、MoveNext() ---将当前遍历的枚举数推进到集合的下一个.
注:如果 MoveNext 越过集合的末尾,则枚举数将被放置在此集合中最后一个元素的后面,而且 MoveNext 返回 false。当枚举数位于此位置时,对MoveNext 的后续调用也返回 false。如果最后一次调用 MoveNext 返回 false,则调用 Current 会引发异常。若要再次将 Current 设置为集合的第一个元素,可以调用 Reset,然后再调用 MoveNext。
(2)、Current属性 ---返回正在遍历的集合中的元素
(3)、Reset() ---重置当前正在遍历的集合中元素的索引.
第三步:具体实现
在介绍完上面两个接口之后,开始具体的实现,现在需要编写一个People类,该类是一个Person集合,要求People类拥有foreach循环的功能,代码如下:
public class People : IEnumerable
{
private Person[] persons;
public People(Person[] persons)
{
Persons = persons;
}
public Person[] Persons { get => persons; set => persons = value; }
public IEnumerator GetEnumerator()
{
return new PersonEnum(Persons);
}
public class PersonEnum : IEnumerator
{
private Person[] perons;
private int _index=-1;
public PersonEnum(Person[] persons)
{
Perons = persons;
}
public object Current => Perons[_index];
public Person[] Perons { get => perons; set => perons = value; }
public bool MoveNext()
{
_index++;
if (_index < perons.Length)
return true;
else
return false;
}
public void Reset()
{
_index = 0;
}
}
}
第四步:验证代码,代码如下:
Person[] persons ={
new Person(){FirstName="Stephen",LastName="Curry"},
new Person(){FirstName="Lebron",LastName="James"},
new Person(){FirstName="Kobe",LastName="Brant"}
};
People people = new People(persons);
foreach (var p in people)
{
Console.WriteLine(((Person)p).LastName);
}
第五步:分析原理
总结分析下上面的代码,实现foreach代码的基本原理如下:
1、编写自定义集合类,实现IEnumerable接口,通过GetEnumerator()方法返回一个迭代器对象实例.
2、通过自定义集合类的构造函数,或者方法,或者属性(都可以),初始化一个类数组 !Important
3、将初始化完的类数组作为参数传递给迭代器类
4、编写迭代器类,create 构造函数,接收自定义集合类初始化完的类数组
5、实现IEnumerator(迭代器)接口,实现对应的三个方法,通过编写三个方法发现,其实迭代器就是简单的对数组进行的操作
第六步:思考
经过上面的分析之后,发现其实foreach语句换成C#代码就是以下代码:
Person[] persons ={
new Person(){FirstName="Stephen",LastName="Curry"},
new Person(){FirstName="Lebron",LastName="James"},
new Person(){FirstName="Kobe",LastName="Brant"}
};
People people = new People(persons);
IEnumerator er = people.GetEnumerator();
while (er.MoveNext())
{
Console.WriteLine(((Person)er.Current).LastName);
}
其实foreach语句就是对数组的只读操作!
- [接口测试 - 基础篇] 04 无法绕过的json解析
- 【专知-PyTorch手把手深度学习教程02】CNN快速理解与PyTorch实现: 图文+代码
- [接口测试 - 基础篇] 03 unittest测试框架了解多少才够?
- 关关的刷题日记09——Leetcode 80. Remove Duplicates from Sorted Array II
- [linux][kernel]dump kmsg到文件系统中
- [接口测试 - 基础篇] 12 还是要掌握python日志管理模块的
- Spring整合Quartz分布式调度
- [接口测试 - 基础篇] 11 掌握下python解析YAML格式也是需要的
- 如何用Python创建眼下火爆的区块链?这篇干货请收藏
- [接口测试 - 基础篇] 10 别啊,还有INI格式呢,别忘记我
- [接口测试 - 基础篇] 09 其实吧,读写csv格式也是要掌握的
- 【源码分享】机器学习之Python支持向量机
- [接口测试 - 基础篇] 08 封装个基本的excel解析类
- 关关的刷题日记10——Leetcode 1. Two Sum 方法1
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Kubernetes 的新武器:层级命名空间
- 手撕distributed ram类型同步FIFO
- 解决PIL透明的图片放在新图片上报错
- Dissonance 使用(一)
- Dissonance 使用(三)
- 从代码角度看各类子域名收集工具
- Shiro-550 PoC 编写日记
- k8s代码走读---client-go编程交互基础
- 故障恢复:一次底层超融合故障导致的异常处理
- JavaSE语法
- 链表:听说过两天反转链表又写不出来了?
- 汇总:MySQL 8.0 运维便捷命令
- 链表:一道题目考察了常见的五个操作!
- 性能测试必备知识(11)- 怎么理解内存中的Buffer和Cache?
- 神经网络-感知器(二):Python代码实现