1. 程式人生 > >C#中foreach語句的迭代器實現機制

C#中foreach語句的迭代器實現機制

C#中的foreach語句可用於迴圈遍歷某個集合中的元素,而所有的只要支援了IEnumerable或IEnumerable<T>泛型介面的型別都是可以

用foreach遍歷的。其具體的遍歷實現過程就是利用C#中的迭代器中的方法來按照特定順序遍歷的。在.NET中IEnumerator和IEnumerator<T>

就是對迭代器的抽象,如果要自定義的型別也支援foreach迴圈則首先須要宣告該類支援IEnumerable或IEnumerable<T>介面,然後再去實現自己

的支援了IEnumerator<T>的迭代器型別;唉,先看程式碼吧!

---------YYC

For Example:

//  實現了IEnumerable<T>泛型介面的類
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Pra6
{
    public enum PhoneType
    { 
     家庭電話,辦公電話,手機,小靈通,傳真
    }

    class Phones : IEnumerable<string>
    {
        private string[] phones;
        private int count = 0;
        public int Count {
            get { return count; }
        }
        public string this[PhoneType type]
        {
            get { 
               return phones[(int)type];
            }
            set { 
              int index  =  (int)type;
              if (phones[index] == null && value != null)
              {
                  count++;
              }
              else
                  if (phones[index] != null && value == null)
                  {
                      count--;
                  }
              phones[index] = value;
            }
        }

        public Phones()
        {
            phones = new string[5];
        }
        public void CopyTo( Array array,int index)
        { 
          foreach(string s in phones )
          {
              if (s != null)
                  array.SetValue(s,index++);
          }
        }
      public   IEnumerator<string > GetEnumerator()//返回迭代器
        {
            return new MyEnumerator<string>(phones);
        }
        IEnumerator IEnumerable.GetEnumerator()//返回迭代器,由於IEnumerable<T>介面也繼承了IEnumerable介面所以出於相容性考慮,最後也要實現該方法,但內容可//以和上面的程式碼一樣
        {
            return this.GetEnumerator();
        }

    }
}

//  實現了IEnumerator<T>的迭代器型別

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
namespace Pra6
{
    class MyEnumerator<T>:IEnumerator<T>
    {
        private int current;
        internal T[] array;
        public T Current {//返回當前資料
            get { return array[current]; }
        }
        public MyEnumerator(T[] array)
        {
            this.array = array;
            this.current = -1;
        }
        public bool MoveNext()//使迭代器移向下一項
        {
            if (++current == array.Length)
                return false ;
            return true;
        }
        object IEnumerator.Current//返回當前的資料,同樣也是出於相容性的考慮
        { 
           get { return array [current];}
        }
        public void Reset()//重設迭代器,以便重新開始遍歷
        {
            current = -1;
        }
        void IDisposable.Dispose()
        { 
        
        }



    }
}

//呼叫過程
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Pra6
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            LinkedList<string> aa = new LinkedList<string>();
            string a1 = "aa";
            string a2 = "bb";
            string a3 = "cc";
            string a4 = "dd";
            LinkedListNode<string> b1 = aa.AddFirst(a1);
            b1 =  aa.AddAfter(b1,a2);
            b1 = aa.AddAfter(b1, a3);
            aa.AddLast(a4);
            textBox1.Text = null ;
            foreach (string a in aa )
            {
                textBox1.Text += "   " + a;
            }
            b1 = b1.Next;
            b1 = b1.Next;
           
            textBox1.Text += b1.Value;

        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }

        private void button2_Click(object sender, EventArgs e)
        {
            Phones ph = new Phones();
            ph[PhoneType.辦公電話] = "111";
            ph[PhoneType.傳真] = "222";
            ph[PhoneType.家庭電話] = "333";
            ph[PhoneType.手機] = "444";
            ph[PhoneType.小靈通] = "555";
            textBox1.Text = null;
            //方法一:呼叫foreach實現
            foreach(string s in ph)
            {
                textBox1.Text += s+"   ";
            }

            //呼叫迭代器實現遍歷。
            IEnumerator<string> iter = ph.GetEnumerator();
            while(iter.MoveNext())
            {
                textBox1.Text += iter.Current + "   ";
            }
        }
    }
}

其實,在.NET的中間語言是並不支援foreach語句的,而是C#的編譯器可以將程式中的foreach語句轉換成

對迭代器操作的while或for語句的迴圈遍歷,例如:上面的:

foreach(string s in ph)
            {
                textBox1.Text += s+"   ";
            }

其真實的執行程式碼就是

IEnumerator<string> iter = ph.GetEnumerator();
            while(iter.MoveNext())
            {
                textBox1.Text += iter.Current + "   ";
            }

所以當我們懂了這個實現原理後我們也是可以去定義我們自己的可以支援foreach的型別了,

但是需要值得一提的是並非我們每次都需要去顯示的自己定義我們的迭代器型別,C#語言本身

一大特色就是快速開發嗎,這是因為C#中為迭代器模式提供了一種簡化實現方式,即使用yield returne語句。

例如:將上面的Phones類中的GetEnumerator()直接改為下面的方式,不用再去定義迭代器型別,就能直接

使用foreach語句:

public IEnumerator<string> GetEnumerator()
      {
          for (int i = 0; i < 5;i++ )
          {
              if (phones[i]!=null)
                  yield return phones[i];
          }
      }