23.迭代器
23.1 知识点
迭代器是什么
迭代器(iterator)有时又称光标(cursor),是程序设计的软件设计模式。迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的标识。
在表现效果上看,迭代器是可以在容器对象(例如链表或数组)上遍历访问的接口。设计人员无需关心容器对象的内存分配的实现细节,可以用foreach遍历的类,都是实现了迭代器的。
标准迭代器的实现方法和使用
class语句块外 namespace语句块内
关键接口:IEnumerator
, IEnumerable
。命名空间:using System.Collections;
。可以通过同时继承IEnumerable
和IEnumerator
实现其中的方法。
实例:
// 自定义列表 同时继承IEnumerable和IEnumerator
class CustomList : IEnumerable, IEnumerator
{
// 整形数组
private int[] intArray;
// 默认从-1开始的光标 用于表示 数据得到了哪个位置
private int position = -1;
// 构造函数初始化整形数组
public CustomList()
{
intArray = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
}
#region IEnumerable里要实现的成员
public IEnumerator GetEnumerator()
{
// 每次foreach前想重置光标位置
Reset();
// 自定义列表类 就继承了IEnumerator 直接返回自己自定义列表类就行
return this;
}
#endregion
#region IEnumerator里要实现的成员
// 根据当前光标位置 返回元素
public object Current
{
get
{
return intArray[position];
}
}
// 移动光标到下一位置
public bool MoveNext()
{
// 移动光标
++position;
// 是否溢出 溢出就不合法
return position < intArray.Length;
}
// reset是重置光标位置 一般写在获取 IEnumerator对象这个函数中
// 用于第一次重置光标位置
public void Reset()
{
position = -1;
}
#endregion
}
主函数内
foreach本质
- 先获取
in
后面这个要遍历的对象的IEnumerator
。 - 执行得到这个要遍历的对象的
IEnumerator
中的MoveNext
移动光标方法。 - 只要
MoveNext
方法的返回值是true
,就会去得到Current
,否则MoveNext
方法的返回值是false
,结束foreach
。 - 然后赋值给
item
。 - 重复2-4 直到
MoveNext
方法的返回值是false
,结束foreach
。
创建一个自定义列表实例
CustomList customList1 = new CustomList();
foreach遍历打印customList1中的元素
foreach (int item in customList1)
{
Console.WriteLine("遍历customList1:" + item);
// 遍历customList1:1
// 遍历customList1:2
// 遍历customList1:3
// 遍历customList1:4
// 遍历customList1:5
// 遍历customList1:6
// 遍历customList1:7
// 遍历customList1:8
}
注意:假如没有实现Reset的逻辑,在此foreach遍历打印会打印不出来,因为光标还是在末尾元素,MoveNext为false。
foreach (int item in customList1)
{
Console.WriteLine("遍历customList1:" + item);
// 遍历customList1:1
// 遍历customList1:2
// 遍历customList1:3
// 遍历customList1:4
// 遍历customList1:5
// 遍历customList1:6
// 遍历customList1:7
// 遍历customList1:8
}
用yield return语法糖实现迭代器
class语句块外 namespace语句块内
yield return
是C#提供给我们的语法糖,所谓语法糖,也称糖衣语法。主要作用就是将复杂逻辑简单化,可以增加程序的可读性,从而减少程序代码出错的机会。
关键接口:IEnumerable
。命名空间:using System.Collections;
。让想要通过foreach遍历的自定义类实现接口中的方法GetEnumerator
即可。
实例:
// 自定义列表2 只继承IEnumerable
class CustomList2 : IEnumerable
{
private int[] intArray2;
public CustomList2()
{
intArray2 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
}
#region IEnumerable里要实现的成员
public IEnumerator GetEnumerator()
{
for (int i = 0; i < intArray2.Length; i++)
{
// yield关键字 配合迭代器使用
// 可以理解为 暂时返回 保留当前的状态
// 一会还会在回来
// C#的语法糖
// 本质上其实还是有 Current属性 MoveNext方法 Reset方法
// 只是系统检测到yield return关键字 帮我们做好了这部分逻辑
yield return intArray2[i];
}
// 这样逐个写也可以
// yield return intArray[0];
// yield return intArray[1];
// yield return intArray[2];
// yield return intArray[3];
// yield return intArray[4];
// yield return intArray[5];
// yield return intArray[6];
// yield return intArray[7];
}
#endregion
}
主函数内
创建一个自定义列表2实例:
CustomList customList2 = new CustomList();
foreach遍历打印customList2中的元素:
foreach (int item in customList2)
{
Console.WriteLine("遍历customList2:" + item);
// 遍历customList2:1
// 遍历customList2:2
// 遍历customList2:3
// 遍历customList2:4
// 遍历customList2:5
// 遍历customList2:6
// 遍历customList2:7
// 遍历customList2:8
}
用yield return语法糖为泛型类实现迭代器
class语句块外 namespace语句块内
泛型自定义列表,其实还是继承IEnumerable
。其他和非泛型大同小异,有泛型的IEnumerable
,暂时用不上也可以。
// 泛型自定义列表
// 其实还是继承IEnumerable 其他和非泛型大同小异
// 有泛型的IEnumerable 暂时用不上也可以
class CustomList<T> : IEnumerable
{
private T[] array;
public CustomList(params T[] array)
{
this.array = array;
}
#region IEnumerable里要实现的成员
public IEnumerator GetEnumerator()
{
for (int i = 0; i < array.Length; i++)
{
yield return array[i];
}
}
#endregion
}
主函数内
创建一个自定义泛型列表实例,传入字符串类型:
CustomList<string> customListString = new CustomList<string>("123", "321", "333", "555");
foreach遍历打印customListString
中的元素:
foreach (string item in customListString)
{
Console.WriteLine("customListString:" + item);
// customListString:123
// customListString:321
// customListString:333
// customListString:555
}
总结
迭代器就是可以让我们在外部直接通过foreach
遍历对象中元素而不需要了解其结构。主要的两种方式:
- 传统方式 继承两个接口 实现里面的方法。
- 用语法糖
yield return
去返回内容 只需要继承一个接口即可。
23.2 知识点代码
using System;
using System.Collections;
namespace Lesson22_迭代器
{
#region 知识点一 迭代器是什么
//迭代器(iterator)有时又称光标(cursor)
//是程序设计的软件设计模式
//迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素
//而又不暴露其内部的标识
//在表现效果上看
//是可以在容器对象(例如链表或数组)上遍历访问的接口
//设计人员无需关心容器对象的内存分配的实现细节
//可以用foreach遍历的类,都是实现了迭代器的
#endregion
//class语句块外 namespace语句块内
#region 知识点二 标准迭代器的实现方法和使用
//关键接口:IEnumerator,IEnumerable
//命名空间:using System.Collections;
//可以通过同时继承IEnumerable和IEnumerator 实现其中的方法
//实例
//自定义列表 同时继承IEnumerable和IEnumerator
class CustomList : IEnumerable, IEnumerator
{
//整形数组
private int[] intArray;
//默认从-1开始的光标 用于表示 数据得到了哪个位置
private int position = -1;
//构造函数初始化整形数组
public CustomList()
{
intArray = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
}
#region IEnumerable里要实现的成员
public IEnumerator GetEnumerator()
{
//每次foreach前想重置光标位置
Reset();
//自定义列表类 就继承了IEnumerator 直接返回自己自定义列表类就行
return this;
}
#endregion
#region IEnumerator里要实现的成员
//根据当前光标位置 返回元素
public object Current
{
get
{
return intArray[position];
}
}
//移动光标到下一位置
public bool MoveNext()
{
//移动光标
++position;
//是否溢出 溢出就不合法
return position < intArray.Length;
}
//reset是重置光标位置 一般写在获取 IEnumerator对象这个函数中
//用于第一次重置光标位置
public void Reset()
{
position = -1;
}
#endregion
}
#endregion
#region 知识点三 用yield return 语法糖实现迭代器
//yield return 是C#提供给我们的语法糖
//所谓语法糖,也称糖衣语法
//主要作用就是将复杂逻辑简单化,可以增加程序的可读性
//从而减少程序代码出错的机会
//关键接口:IEnumerable
//命名空间:using System.Collections;
//让想要通过foreach遍历的自定义类实现接口中的方法GetEnumerator即可
//实例
//自定义列表2 只继承IEnumerable
class CustomList2 : IEnumerable
{
private int[] intArray2;
public CustomList2()
{
intArray2 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
}
#region IEnumerable里要实现的成员
public IEnumerator GetEnumerator()
{
for (int i = 0; i < intArray2.Length; i++)
{
//yield关键字 配合迭代器使用
//可以理解为 暂时返回 保留当前的状态
//一会还会在回来
//C#的语法糖
//本质上其实还是有 Current属性 MoveNext方法 Reset方法
//只是系统检测到yield return关键字 帮我们做好了这部分逻辑
yield return intArray2[i];
}
//这样逐个写也可以
//yield return intArray[0];
//yield return intArray[1];
//yield return intArray[2];
//yield return intArray[3];
//yield return intArray[4];
//yield return intArray[5];
//yield return intArray[6];
//yield return intArray[7];
}
#endregion
}
#endregion
#region 知识点四 用yield return 语法糖为泛型类实现迭代器
//泛型自定义列表
//其实还是继承IEnumerable 其他和非泛型大同小异
//有泛型的IEnumerable 暂时用不上也可以
class CustomList<T> : IEnumerable
{
private T[] array;
public CustomList(params T[] array)
{
this.array = array;
}
#region IEnumerable里要实现的成员
public IEnumerator GetEnumerator()
{
for (int i = 0; i < array.Length; i++)
{
yield return array[i];
}
}
#endregion
}
#endregion
class Program
{
static void Main(string[] args)
{
Console.WriteLine("迭代器");
//主函数内
#region 知识点二 标准迭代器的实现方法和使用
//foreach本质
//1.先获取 in后面 这个 要遍历的对象的 IEnumerator
//会调用对象其中的GetEnumerator方法 来获取 要遍历的对象的 IEnumerator
//所以其实继不继承IEnumerable其实都可以 继承IEnumerable无非就是提醒你实现GetEnumerator方法
//只要 要遍历的对象 实现了GetEnumerator方法 就可以使用foreach
//2.执行得到 这个 要遍历的对象的 IEnumerator 中的 MoveNext移动光标方法
//3.只要MoveNext方法的返回值时true 就会去得到Current 否则MoveNext方法的返回值时false 结束foreach
//4.然后赋值给item
//5.重复2-4 直到MoveNext方法的返回值时false 结束foreach
//创建一个自定义列表实例
CustomList customList1 = new CustomList();
//foreach遍历打印customList1中的元素
foreach (int item in customList1)
{
Console.WriteLine("遍历customList1:" + item);
//遍历customList1:1
//遍历customList1:2
//遍历customList1:3
//遍历customList1:4
//遍历customList1:5
//遍历customList1:6
//遍历customList1:7
//遍历customList1:8
}
//注意:假如没有实现Reset的逻辑 在此foreach遍历打印会打印不出来 因为光标还是在末尾元素 MoveNext为false
foreach (int item in customList1)
{
Console.WriteLine("遍历customList1:" + item);
//遍历customList1:1
//遍历customList1:2
//遍历customList1:3
//遍历customList1:4
//遍历customList1:5
//遍历customList1:6
//遍历customList1:7
//遍历customList1:8
}
#endregion
#region 知识点三 用yield return 语法糖实现迭代器
//创建一个自定义列表2实例
CustomList customList2 = new CustomList();
//foreach遍历打印customList2中的元素
foreach (int item in customList2)
{
Console.WriteLine("遍历customList2:" + item);
//遍历customList2:1
//遍历customList2:2
//遍历customList2:3
//遍历customList2:4
//遍历customList2:5
//遍历customList2:6
//遍历customList2:7
//遍历customList2:8
}
#endregion
#region 知识点四 用yield return 语法糖为泛型类实现迭代器
//创建一个自定义泛型列表实例 传入字符串类型
CustomList<string> customListString = new CustomList<string>("123", "321", "333", "555");
//foreach遍历打印customListString中的元素
foreach (string item in customListString)
{
Console.WriteLine("customListString:" + item);
//customListString:123
//customListString:321
//customListString:333
//customListString:555
}
#endregion
}
}
}
//总结:
//迭代器就是可以让我们在外部直接通过foreach遍历对象中元素而不需要了解其结构
//主要的两种方式
//1.传统方式 继承两个接口 实现里面的方法
//2.用语法糖 yield return 去返回内容 只需要继承一个接口即可
23.3 练习题
请为一个自定义类用两种方法,让其可以被foreach遍历
class语句块外 namespace语句块内
标准迭代器
// 自定义列表 同时继承IEnumerable和IEnumerator
class CustomList : IEnumerable, IEnumerator
{
// 整形数组
private int[] intArray;
// 默认从-1开始的光标 用于表示 数据得到了哪个位置
private int position = -1;
// 构造函数初始化整形数组
public CustomList()
{
intArray = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
}
#region IEnumerable里要实现的成员
public IEnumerator GetEnumerator()
{
// 每次foreach前想重置光标位置
Reset();
// 自定义列表类 就继承了IEnumerator 直接返回自己自定义列表类就行
return this;
}
#endregion
#region IEnumerator里要实现的成员
// 根据当前光标位置 返回元素
public object Current
{
get
{
return intArray[position];
}
}
// 移动光标到下一位置
public bool MoveNext()
{
// 移动光标
++position;
// 是否溢出 溢出就不合法
return position < intArray.Length;
}
// reset是重置光标位置 一般写在获取 IEnumerator对象这个函数中
// 用于第一次重置光标位置
public void Reset()
{
position = -1;
}
#endregion
}
用yield return 语法糖实现迭代器
// 自定义列表2 只继承IEnumerable
class CustomList2 : IEnumerable
{
private int[] intArray2;
public CustomList2()
{
intArray2 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
}
#region IEnumerable里要实现的成员
public IEnumerator GetEnumerator()
{
for (int i = 0; i < intArray2.Length; i++)
{
// yield关键字 配合迭代器使用
// 可以理解为 暂时返回 保留当前的状态
// 一会还会在回来
// C#的语法糖
// 本质上其实还是有 Current属性 MoveNext方法 Reset方法
// 只是系统检测到yield return关键字 帮我们做好了这部分逻辑
yield return intArray2[i];
}
}
#endregion
}
主函数内
// 创建一个自定义列表实例
CustomList customList1 = new CustomList();
// foreach遍历打印customList1中的元素
foreach (int item in customList1)
{
Console.WriteLine("遍历customList1:" + item);
// 遍历customList1:1
// 遍历customList1:2
// 遍历customList1:3
// 遍历customList1:4
// 遍历customList1:5
// 遍历customList1:6
// 遍历customList1:7
// 遍历customList1:8
}
// 注意:假如没有实现Reset的逻辑 在此foreach遍历打印会打印不出来 因为光标还是在末尾元素 MoveNext为false
foreach (int item in customList1)
{
Console.WriteLine("遍历customList1:" + item);
// 遍历customList1:1
// 遍历customList1:2
// 遍历customList1:3
// 遍历customList1:4
// 遍历customList1:5
// 遍历customList1:6
// 遍历customList1:7
// 遍历customList1:8
}
// 创建一个自定义列表2实例
CustomList customList2 = new CustomList();
// foreach遍历打印customList2中的元素
foreach (int item in customList2)
{
Console.WriteLine("遍历customList2:" + item);
// 遍历customList2:1
// 遍历customList2:2
// 遍历customList2:3
// 遍历customList2:4
// 遍历customList2:5
// 遍历customList2:6
// 遍历customList2:7
// 遍历customList2:8
}
23.4 练习题代码
using System;
using System.Collections;
namespace Lesson22_练习题
{
//class语句块外 namespace语句块内
#region 练习题一
//请为一个自定义类
//用两种方法让其可以被foreach遍历
//标准迭代器
//自定义列表 同时继承IEnumerable和IEnumerator
class CustomList : IEnumerable, IEnumerator
{
//整形数组
private int[] intArray;
//默认从-1开始的光标 用于表示 数据得到了哪个位置
private int position = -1;
//构造函数初始化整形数组
public CustomList()
{
intArray = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
}
#region IEnumerable里要实现的成员
public IEnumerator GetEnumerator()
{
//每次foreach前想重置光标位置
Reset();
//自定义列表类 就继承了IEnumerator 直接返回自己自定义列表类就行
return this;
}
#endregion
#region IEnumerator里要实现的成员
//根据当前光标位置 返回元素
public object Current
{
get
{
return intArray[position];
}
}
//移动光标到下一位置
public bool MoveNext()
{
//移动光标
++position;
//是否溢出 溢出就不合法
return position < intArray.Length;
}
//reset是重置光标位置 一般写在获取 IEnumerator对象这个函数中
//用于第一次重置光标位置
public void Reset()
{
position = -1;
}
#endregion
}
//用yield return 语法糖实现迭代器
//自定义列表2 只继承IEnumerable
class CustomList2 : IEnumerable
{
private int[] intArray2;
public CustomList2()
{
intArray2 = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
}
#region IEnumerable里要实现的成员
public IEnumerator GetEnumerator()
{
for (int i = 0; i < intArray2.Length; i++)
{
//yield关键字 配合迭代器使用
//可以理解为 暂时返回 保留当前的状态
//一会还会在回来
//C#的语法糖
//本质上其实还是有 Current属性 MoveNext方法 Reset方法
//只是系统检测到yield return关键字 帮我们做好了这部分逻辑
yield return intArray2[i];
}
//这样逐个写也可以
//yield return intArray[0];
//yield return intArray[1];
//yield return intArray[2];
//yield return intArray[3];
//yield return intArray[4];
//yield return intArray[5];
//yield return intArray[6];
//yield return intArray[7];
}
#endregion
}
#endregion
class Program
{
static void Main(string[] args)
{
Console.WriteLine("迭代器练习题");
//主函数内
#region 练习题一
//创建一个自定义列表实例
CustomList customList1 = new CustomList();
//foreach遍历打印customList1中的元素
foreach (int item in customList1)
{
Console.WriteLine("遍历customList1:" + item);
//遍历customList1:1
//遍历customList1:2
//遍历customList1:3
//遍历customList1:4
//遍历customList1:5
//遍历customList1:6
//遍历customList1:7
//遍历customList1:8
}
//注意:假如没有实现Reset的逻辑 在此foreach遍历打印会打印不出来 因为光标还是在末尾元素 MoveNext为false
foreach (int item in customList1)
{
Console.WriteLine("遍历customList1:" + item);
//遍历customList1:1
//遍历customList1:2
//遍历customList1:3
//遍历customList1:4
//遍历customList1:5
//遍历customList1:6
//遍历customList1:7
//遍历customList1:8
}
//创建一个自定义列表2实例
CustomList customList2 = new CustomList();
//foreach遍历打印customList2中的元素
foreach (int item in customList2)
{
Console.WriteLine("遍历customList2:" + item);
//遍历customList2:1
//遍历customList2:2
//遍历customList2:3
//遍历customList2:4
//遍历customList2:5
//遍历customList2:6
//遍历customList2:7
//遍历customList2:8
}
#endregion
}
}
}
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com