在学习协同时,看到一篇文章讲解枚举数(Enumerator)和可枚举类型(Enumerable)。于是就自己在脚本中实验了。并做了自己的修改。

public class IEnumerator_Test : MonoBehaviour 
{
    // C#的枚举数(Enumerator)和可枚举类型(Enumerable)
    // 数组可以被foreach语句遍历数组中的元素,原因是数组可以按需提供一个叫做枚举数(enumerator)的对象.枚举数可以依次返回请求的数组的元素. 
    // 对于有枚举数的类型而言,必须有一个方法来获取它们.在.NET中获取一个对象枚举数的标准用法是调用对象的GetEnumerator方法.实现GetEnumerator方法的类型叫做可枚举类型(enumerable),数组就是可枚举类型. 

    // 要注意枚举数(enumerator)和可枚举类型(enumerable)的区别和联系. 
    // 枚举数是可以依次返回集合项的类对象,可枚举类型是带有GetEnumerator方法的类型,它返回枚举数. 
    // 当foreach被用来遍历可枚举类型时,它就会执行如下的行为: 
    // 1.通过调用GetEnumerator方法获取对象的枚举数. 
    // 2.从枚举数中请求每一项并且把它作为迭代变量,代码可以读但不可以改变. 

    //  枚举数共有3种,可以用以下方式来实现枚举数: 
    //  1 IEnumerator/IEnumerable接口,叫做非泛型接口形式. 
    //  2 IEnumerator
   
   
    
    /IEnumerable
    
    
     
     接口,叫做泛型接口形式. 
    //  3 不使用接口形式. 

    //  IEnumerator接口 
    //  IEnumerator接口包含3个函数成员:Current,MoveNext,Reset 
    //  Current:返回序列中当前项的属性,它是一个只读属性.返回object类型的引用,所以可以返回任何类型. 
    //  MoveNext:是把枚举数位置前进到集合的下一项的方法,它返回布尔值,指示新位置是有效位置还是已经超过了序列的尾部.如果是已经到达了尾部,则返回false. 
    //  Reset:方法把位置重置为原始状态. 

    void Start () 
	{
	    //调用枚举数的Current和MoveNext成员函数手工遍历数组中的元素
        int[] num = new int[5] {1, 3, 5, 7, 9};
        IEnumerator ie = num.GetEnumerator(); //获得枚举数
        while (ie.MoveNext()) //移到下一项 (枚举数的初始位置是-1,所以要想获取第一个元素就必须执行MoveNext()方法)
        {
            Debug.Log(ie.Current);
        }
	}
}using UnityEngine;
using System.Collections;
using System;

//颜色名数组的枚举数类
class ColorEnumerator : IEnumerator
{
    private string[] Colors;
    private int Position = -1;
    public object Current
    {
        get
        {
            if (Position == -1)
            {
                //如果调用方法失败不是由无效参数造成的,则使用 InvalidOperationException。例如,对于 InvalidOperationException 而言:
                //如果创建枚举数后修改了集合中的对象,则由 MoveNext 引发该异常。
                //如果执行方法调用前关闭了资源集,则由 GetString 引发该异常。

                //如果方法调用失败是由无效参数造成的,则应改为引发 ArgumentException 或其派生类 ArgumentNullException 或 ArgumentOutOfRangeException。
                //LdfldaMicrosoft 中间语言 (MSIL)指令引发 InvalidOperationException。
                throw new InvalidOperationException(); //InvalidOperationException当方法调用对于对象的当前状态无效时引发的异常。
            }
            if (Position == Colors.Length)
                throw new InvalidOperationException();
            return Colors[Position];
        }
    }

    public bool MoveNext()
    {
        if (Position < Colors.Length - 1)
        {
            Position++;
            return true;
        }
        else
            return false;
    }

    public void Reset()
    {
        Position = -1;
    }

    public ColorEnumerator(string[] theColors)  //构造函数 
    {
        Colors = new string[theColors.Length];
        for (int i = 0; i < theColors.Length; i++)
            Colors[i] = theColors[i];
    }
}

public class IEnumerator_Test1 : MonoBehaviour
{
    void Start()
    {
        ColorEnumerator ce = new ColorEnumerator(new string[] { "yellow", "red", "white", "black" });
        while (ce.MoveNext())
            Debug.Log(ce.Current);
    }
}



“如果要用foreach来遍历枚举数,需要再定义一个可枚举类型的类,这个类要实现IEnumerable接口的功能.”代码是如下的样子。


//颜色名数组的枚举数类
class ColorEnumerator : IEnumerator,IEnumerable
{
    private string[] Colors;
    private int Position = -1;
    public object Current
    {
        get
        {
            if (Position == -1)
                throw new InvalidOperationException();
            if (Position == Colors.Length)
                throw new InvalidOperationException();
            return Colors[Position];
        }
    }

    public bool MoveNext()
    {
        if (Position < Colors.Length - 1)
        {
            Position++;
            return true;
        }
        else
            return false;
    }

    public void Reset()
    {
        Position = -1;
    }

    public IEnumerator GetEnumerator()
    {
        return this;
    }

    public ColorEnumerator(string[] theColors)  //构造函数 
    {
        Colors = new string[theColors.Length];
        for (int i = 0; i < theColors.Length; i++)
            Colors[i] = theColors[i];
    }
}

//如果要用foreach来遍历枚举数,需要再定义一个可枚举类型的类,这个类要实现IEnumerable接口的功能. 
class MyEnumerable : IEnumerable  //定义一个可枚举类型的类 
{
    string[] MyColor = new string[] { "blue", "green", "purple", "pink" };
    public IEnumerator GetEnumerator()    //实现接口        
    {
        return new ColorEnumerator(MyColor); //返回一个枚举数的对象 
    }
}

public class IEnumerator_Test1 : MonoBehaviour
{
    void Start()
    {
        MyEnumerable me = new MyEnumerable();
        foreach (string i in me)
            Debug.Log(i);
    }

    // Update is called once per frame
    void Update()
    {

    }
}



上面代码中要使用foreach遍历,需要新定义一个可枚举类且实现IEnumerable接口。但是,我将代码改成下面的样子,也能实现。


using UnityEngine;
using System.Collections;
using System;

//颜色名数组的枚举数类
class ColorEnumerator : IEnumerator,IEnumerable
{
    private string[] Colors;
    private int Position = -1;
    public object Current
    {
        get
        {
            if (Position == -1)
                throw new InvalidOperationException();
            if (Position == Colors.Length)
                throw new InvalidOperationException();
            return Colors[Position];
        }
    }

    public bool MoveNext()
    {
        if (Position < Colors.Length - 1)
        {
            Position++;
            return true;
        }
        else
            return false;
    }

    public void Reset()
    {
        Position = -1;
    }
    
    //实现了才能使用foreach遍历
    public IEnumerator GetEnumerator()
    {
        return this;
    }

    public ColorEnumerator(string[] theColors)  //构造函数 
    {
        Colors = new string[theColors.Length];
        for (int i = 0; i < theColors.Length; i++)
            Colors[i] = theColors[i];
    }
}

public class IEnumerator_Test1 : MonoBehaviour
{
    void Start()
    {
        ColorEnumerator ce = new ColorEnumerator(new string[] { "yellow", "red", "white", "black" });
        while (ce.MoveNext())
            Debug.Log(ce.Current);
            
        ce.Reset(); //如果这里不重置,那么后面的foreach遍历就无效了,因为枚举数当前位置经过上面的while循环后已经是最后一个了位置了。
        //同样foreach遍历之后,枚举数的当前位置会是最后一个。
        foreach (string i in ce)
            Debug.Log(i);
    }

    // Update is called once per frame
    void Update()
    {

    }
}



如果有大神些发现代码哪有错误或者任何不妥的地方,请指出。