之所以數組可以使用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