IEnumerable :这是一个标记性接口 ,作用是告诉 C# 编译器:「这个对象是可以被foreach循环遍历的」,IEnumerable接口是非常的简单,只包含一个抽象的方法GetEnumerator(),它返回一个可用于循环访问集合的IEnumerator对象。
IEnumerator :这是一个功能接口,是实际执行遍历操作的 "工具",负责一步步获取集合中的元素。IEnumerator对象有什么呢?它是一个真正的集合访问器,没有它,就不能使用foreach语句遍历集合或数组,因为只有IEnumerator对象才能访问集合中的项,假如连集合中的项都访问不了,那么进行集合的循环遍历是不可能的事情了。
IEnumerator包含一个属性两个方法
MoveNext:把当前的项移动到下一项(类似于索引值),返回一个bool值,这个bool值用来检查当前项是否超出了枚举数的范围!
Current:获取当前项的值,返回一个object的类型!
Reset:顾名思义也就是把一些值恢复为默认值,比如把当前项恢复到默认状态值!
IEnumerable和IEnumerator区别
两者的职责完全不同
IEnumerable:「标记接口」,表示 "这个对象可以被遍历",核心方法 GetEnumerator() 用于提供枚举器。
IEnumerator:「功能接口」,表示 "这个对象是遍历工具",核心方法 MoveNext()/ 属性 Current 用于实际执行遍历。
通俗理解:IEnumerable 是 "水果篮"(可被遍历),IEnumerator 是 "取水果的夹子"(执行遍历),你不能直接遍历 "夹子",只能遍历 "水果篮"。
通过以下实例了解两者的区别:
一般写法(依赖内部集合的GetEnumerator()):
cs
using System;
using System.Collections;
using System.Collections.Generic;
// 1. 自定义实体类(作为泛型类型参数)
public class Student
{
// 学生属性
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"学生ID:{Id},姓名:{Name},年龄:{Age}";
}
}
// 2. 自定义集合类,实现IEnumerable<T>(泛型接口),表示对象可以被遍历
public class StudentCollection : IEnumerable<Student>
{
private List<Student> _studentList = new List<Student>();
public StudentCollection()
{
// 添加测试数据
_studentList.Add(new Student { Id = 1, Name = "张三", Age = 18 });
_studentList.Add(new Student { Id = 2, Name = "李四", Age = 19 });
_studentList.Add(new Student { Id = 3, Name = "王五", Age = 20 });
}
// 3. 实现IEnumerable<Student>的核心方法:GetEnumerator()
public IEnumerator<Student> GetEnumerator()
{
// 直接返回内部List的泛型枚举器(简化实现,无需手动编写枚举器),获得遍历工具
return _studentList.GetEnumerator();
}
// 4. 显式实现非泛型IEnumerable的GetEnumerator()方法(接口继承要求)
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
// 测试类
class Program
{
static void Main(string[] args)
{
StudentCollection studentCol = new StudentCollection();
foreach (Student stu in studentCol)
{
Console.WriteLine(stu);
}
Console.ReadKey();
}
}
装逼写法(手动实现GetEnumerator()的核心逻辑):
cs
using System;
using System.Collections;
using System.Collections.Generic;
// 学生实体类
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"学生ID:{Id},姓名:{Name},年龄:{Age}";
}
}
// 1. 自定义学生集合(实现IEnumerable<Student>),表示对象可以被遍历
public class StudentCollection : IEnumerable<Student>
{
// 内部数据源(仅存储数据,不依赖其枚举器)
private Student[] _studentArray;
// 构造函数:初始化学生数据
public StudentCollection()
{
_studentArray = new Student[]
{
new Student { Id = 1, Name = "张三", Age = 18 },
new Student { Id = 2, Name = "李四", Age = 19 },
new Student { Id = 3, Name = "王五", Age = 20 }
};
}
// 2. 实现IEnumerable<Student>的GetEnumerator(),返回自定义枚举器
public IEnumerator<Student> GetEnumerator()
{
// 不返回内部数组的枚举器,而是返回我们自定义的枚举器实例,获得遍历工具
return new StudentEnumerator(_studentArray);
}
// 显式实现非泛型GetEnumerator(兼容要求)
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
// 3. 自定义枚举器类(定义遍历工具 手动维护遍历逻辑)
private class StudentEnumerator : IEnumerator<Student>
{
// 引用外部集合的数据源
private Student[] _students;
// 遍历状态:当前索引(初始值-1,表示未开始遍历)
private int _currentIndex = -1;
// 构造函数:接收数据源
public StudentEnumerator(Student[] students)
{
_students = students;
}
//泛型Current属性(显式实现,接口)
public Student Current
{
get
{
if (_currentIndex < 0 || _currentIndex >= _students.Length)
{
throw new InvalidOperationException("当前无有效元素");
}
return _students[_currentIndex];
}
}
// 非泛型Current属性(显式实现,接口)
object IEnumerator.Current => Current;
//实现MoveNext():更新遍历状态,判断是否有下一个元素,接口
public bool MoveNext()
{
// 索引自增,判断是否超出数组长度
if (_currentIndex < _students.Length - 1)
{
_currentIndex++;
return true; // 有下一个元素
}
return false; // 遍历完毕
}
// 实现Reset():重置遍历状态(可选,多数场景不使用),接口
public void Reset()
{
_currentIndex = -1;
}
//实现Dispose():释放资源
public void Dispose()
{
// 若有非托管资源,在此处释放
}
}
}
// 测试类
class Program
{
static void Main(string[] args)
{
StudentCollection studentCol = new StudentCollection();
// foreach遍历(底层调用自定义的枚举器)
foreach (Student stu in studentCol)
{
Console.WriteLine(stu);
}
}
}
还有在GetEnumerator()中使用yield return(简化自定义):
cs
using System;
using System.Collections;
using System.Collections.Generic;
// 学生实体类
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public override string ToString()
{
return $"学生ID:{Id},姓名:{Name},年龄:{Age}";
}
}
// 自定义学生集合(实现IEnumerable<Student>)
public class StudentCollection : IEnumerable<Student>
{
private Student[] _studentArray;
public StudentCollection()
{
_studentArray = new Student[]
{
new Student { Id = 1, Name = "张三", Age = 18 },
new Student { Id = 2, Name = "李四", Age = 19 },
new Student { Id = 3, Name = "王五", Age = 20 }
};
}
// 手动实现GetEnumerator()逻辑,不返回内置集合的枚举器
public IEnumerator<Student> GetEnumerator()
{
// 正序遍历(自定义索引控制,无依赖内置枚举器)
for (int i = 0; i < _studentArray.Length; i++)
{
yield return _studentArray[i]; // 逐个返回元素,编译器自动生成状态机
}
}
// 显式实现非泛型GetEnumerator,接口要求
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
// 测试类
class Program
{
static void Main(string[] args)
{
StudentCollection studentCol = new StudentCollection();
foreach (Student stu in studentCol)
{
Console.WriteLine(stu);
}
}
}
实现foreach遍历
1.继承IEnumerable接口,支持foreach遍历
MyInt类的核心是规范实现IEnumerable接口,将遍历职责委托给内部数组的内置枚举器 ,无需手动编写枚举器的核心逻辑(MoveNext()、Current)。
cs
public class MyInt : IEnumerable
{
// 内部存储的数据源:整型数组
int[] temp = { 1, 32, 43, 343 };
// 实现IEnumerable接口的唯一抽象方法:GetEnumerator()
public IEnumerator GetEnumerator()
{
// 直接返回内部数组的枚举器
return temp.GetEnumerator();
}
}
public class TestClass
{
public static void Test_MyInt()
{
MyInt temp = new MyInt();
foreach (int item in temp)
Console.WriteLine(item);
}
}
2.通过通过返回IEnumerable<int>类型的迭代器方法间接支持foreach遍历
MyInt2类未实现任何可遍历接口,而是通过返回IEnumerable<int>类型的迭代器方法,间接支持foreach遍历,核心依赖yield return关键字的惰性执行特性。
cs
public class MyInt2
{
int[] temp = { 1, 32, 43, 343 };
public IEnumerable<int> GetEnumer()
{
int index = -1;
while (index < temp.Length - 1)
{
index++;
yield return temp[index];
}
}
}
public class TestClass
{
public static void Test_MyInt2()
{
MyInt2 temp = new MyInt2();
var IEnumeratorValue= temp.GetEnumer();
foreach (int item in IEnumeratorValue)
Console.WriteLine(item);
}
}
| 对比维度 | MyInt类 |
MyInt2类 |
|---|---|---|
| 可遍历接口实现 | 实现非泛型IEnumerable接口 |
未实现任何可遍历接口 |
| 底层实现原理 | 委托数组内置枚举器(无yield关键字) |
编译器自动生成状态机(yield return迭代器) |
| 实现复杂度 | 简单(仅实现一个接口方法) | 极简(无需实现接口,仅编写迭代器逻辑) |
| 可读性 | 高(符合可遍历类规范,直观易懂) | 中等(需理解迭代器方法的使用场景) |