之所以数组可以使用foreach来遍历里面的值,原因是数组可以按需提供一个叫做枚举器的对象,枚举器可以依次返回请求的数组中的元素,对于有枚举器的类型而言,获取一个对象枚举器的方法时调用对象的GetEnumerator方法,实现GetEnumerator方法的类型叫做可枚举类型,数组是可枚举类型。
IEnumerator接口(枚举器)
实现IEnumerator的枚举器包含3个函数成员,分别是Current、MoverNext、Reset
(1)Current:返回序列中当前位置的属性,它是只读属性,它返回object类型的引用,所以可以返回任何类型
(2)MoverNext:是把枚举器位置前进到集合中下一项的方法,它返回布尔值,指示新的位置是有效位置(返回true),或者是无效位置,比如当前位置到达了尾部(返回false),枚举器的原始位置在序列中的第一项之前,因此MoveNext必须在第一次使用Current之前调用
(3)Reset:是把位置重置为原始状态的方法
自实现foreach函数的功能
using System;
using System.Collections; //使用IEnumerator类型需要添加
namespace lianxi
{
public class Program
{
static void Main(string[] args)
{
int[] MyArray = { 10, 11, 12, 13 };
IEnumerator ie = MyArray.GetEnumerator(); //获取枚举器
while(ie.MoveNext()) //先进行移动,枚举器原始位置是-1(也就是在集合的第一个元素之前)
{
int i = (int)ie.Current;
Console.WriteLine(i);
}
}
}
}
IEnumerable接口
可枚举类是指实现了IEnumerable接口的类,IEnumerable接口只有一个成员----GetEnumerator方法,它返回对象的枚举器。
IEnumerable和IEnumerator的使用示例(非泛型)
using System;
using System.Collections; //使用IEnumerator、IEnumerable需要添加
namespace lianxi
{
class ColorEnumerator:IEnumerator //实现枚举器
{
string[] _colors;
int _position = -1;
public ColorEnumerator(string[] theColors) //构造函数
{
_colors = new string[theColors.Length];
for (int i = 0; i < theColors.Length; i++)
_colors[i] = theColors[i];
}
public object Current //实现Current
{
get
{
if (_position == -1)
throw new InvalidOperationException();
if(_position >=_colors.Length)
throw new InvalidOperationException();
return _colors[_position];
}
}
public bool MoveNext() //实现MoveNext
{
if (_position < _colors.Length)
{
_position++;
return true;
}
else
return false;
}
public void Reset() //实现Reset
{
_position = -1;
}
}
class Spectrum:IEnumerable //实现可枚举类 此处不继承自IEnumerable也可以foreach遍历
{
string[] Color = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };
public IEnumerator GetEnumerator()
{
return new ColorEnumerator(Color);
}
}
public class Program
{
static void Main(string[] args)
{
Spectrum spectrum = new Spectrum();
foreach (var v in spectrum)
Console.WriteLine(v);
}
}
}
正常我们实现可枚举类时,都会选择让这个自定义的类继承自IEnumerable,然后按以上代码的方式来定义,但其实上它不继承自IEnumerable,也可以实现可枚举,也可以使用foreach遍历,只要该类定义了GetEnumerator函数,并且该函数返回类型是IEnumerator的就可以满足条件。
public class A //无继承
{
public IEnumerator GetEnumerator() //实现GetEnumerator函数,且返回类型为IEnumerator
{
return new List<int>() { 1,2,3,4,5}.GetEnumerator();
}
}
public class Program
{
static void Main(string[] args)
{
A a = new A();
foreach (var item in a)
Console.WriteLine(item);
}
}
泛型枚举接口
在大多数情况下应该使用泛型版本IEnumerable<T>和IEnumerator<T>,它与非泛型的区别如下:
泛型接口的枚举器是类型安全的,它返回实际类型的引用,如果要创建自己的可枚举类,应该实现这些泛型接口。
using System;
using System.Collections; //使用IEnumerator、IEnumerable需要添加
using System.Collections.Generic; //使用泛型IEnumerator<T>、IEnumerable<T>需要添加
namespace lianxi
{
class ColorEnumerator:IEnumerator<string> //实现泛型枚举器
{
private string[] _colors;
private int _position;
public ColorEnumerator(string[] theColors) //构造函数
{
_position = -1;
_colors = new string[theColors.Length];
for (int i = 0; i < theColors.Length; i++)
_colors[i] = theColors[i];
}
public string Current //实现泛型(IEunmerator<T>)的Current
{
get
{
if (_position == -1)
throw new InvalidOperationException();
if(_position >=_colors.Length)
throw new InvalidOperationException();
return _colors[_position];
}
}
object IEnumerator.Current //实现非泛型(IEumerator)的Current
{
get
{
return this.Current; //返回泛型的Current
}
}
public bool MoveNext() //实现MoveNext
{
if (_position < _colors.Length)
{
_position++;
return true;
}
else
return false;
}
public void Reset(){_position = -1;}
void IDisposable.Dispose() { } //实现泛型的另一个继承接口中的函数
}
class Spectrum:IEnumerable<string> //实现泛型可枚举类 不继承IEnumerable<string> 也可以
{
private string[] Color = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };
public IEnumerator<string> GetEnumerator() //实现泛型的GetEnumerator
{
return new ColorEnumerator(Color);
}
IEnumerator IEnumerable.GetEnumerator() //实现非泛型的GetEnumerator
{
return this.GetEnumerator();
}
}
public class Program
{
static void Main(string[] args)
{
Spectrum spectrum = new Spectrum();
foreach (var v in spectrum)
Console.WriteLine(v);
}
}
}
继承泛型枚举接口参考自:https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.generic.ienumerable-1?view=netframework-4.8