close

首先, 以ArrayList為例,透過foreach指令來依序讀取內容物

   1: ArrayList list = new ArrayList();
   2: list.Add("1");
   3: list.Add(2);
   4: list.Add("3");
   5: list.Add('4');
   6: foreach(object o in list)
   7: {
   8:     Console.Write($"{o.GetType().ToString()} :");
   9:     Console.WriteLine(o);
  10: }

image

假設我們有一個簡單的函式如下,希望每次呼叫依序回傳1, 2, 3…

   1: static int SimpleReturn()
   2: {
   3:   return 1;
   4:   return 2;
   5:   return 3;
   6: }

如果用下列方式呼叫SimpleReturn()

   1: static void Demo4()
   2:  {
   3:      Console.WriteLine(SimpleReturn());
   4:      Console.WriteLine(SimpleReturn());
   5:      Console.WriteLine(SimpleReturn());
   6:      Console.WriteLine(SimpleReturn());
   7:  }

似乎只會回傳遞一種結果

image

如果將SimpleReturn修改成YieldReturn()如下

   1: static IEnumerable<int> YieldReturn()
   2: {
   3:     yield return 1;
   4:     yield return 2;
   5:     yield return 3;
   6: }

用下列方式呼叫YieldReturn()

   1: static void Demo5()
   2: {
   3:   foreach(int i in YieldReturn())
   4:   Console.WriteLine(i);
   5: }

結果如下,可以依序取出1, 2, 3,…

image

-------------------------------------------------------------------------------------------------------------------------------

1. MyArrayList 繼承 IEnumerable介面,其中GetEnumerator()需要自行實作。

   1: class MyArrayList : IEnumerable
   2: {
   3:     public IEnumerator GetEnumerator()
   4:     {
   5:         throw new NotImplementedException();
   6:     }
   7: }

2. 建立MyArrayList 私有變數成員(m_items、index)及創建子

   1: class MyArrayList : IEnumerable
   2: {
   3:    // 儲存陣列
   4:    object[] m_items = null;
   5:    // 陣列索引
   6:    int index = 0;
   7:  
   8:    /// <summary>
   9:    /// MyArrayList創建子
  10:    /// </summary>
  11:    public MyArrayList()
  12:    {
  13:        // For the sake of simplicity lets keep them as arrays
  14:        // ideally it should be link list
  15:        m_items = new object[100];
  16:    }
  17:    public IEnumerator GetEnumerator()
  18:    {
  19:        throw new NotImplementedException();
  20:    }
  21: }

3. 加入public method: Add() 提供物件加入陣列

   1: public void Add(object item)
   2: {
   3:     m_items[index] = item;
   4:     index++;
   5: }

 

4. 重新定義GetEnumerator:

如同前面YieldReturn()例子, 我們希望提供一個method每呼叫一次,可以取得m_items的一個元素,

並且索引值自動移動至下一個元素(yield), 下次呼叫則傳回該索引值所指的元素(return),重複這些動作直到取完為止(o==null)。

   1: public IEnumerator GetEnumerator()
   2: {
   3:     foreach(object o in m_items)
   4:     {
   5:         // Lets check for end of list (its bad code since we used arrays)
   6:         if (o==null)
   7:         {
   8:             break;
   9:         }
  10:         // Return the current element and then on next function call 
  11:         // resume from next element rather than starting all over again;
  12:         yield return o;
  13:     }
  14: }

 

綜合1~4程式碼

   1: class MyArrayList : IEnumerable
   2: {
   3:     // 儲存陣列
   4:     object[] m_items = null;
   5:     // 陣列索引
   6:     int index = 0;
   7:  
   8:     /// <summary>
   9:     /// MyArrayList創建子
  10:     /// </summary>
  11:     public MyArrayList()
  12:     {
  13:         // For the sake of simplicity lets keep them as arrays
  14:         // ideally it should be link list
  15:         m_items = new object[100];
  16:     }
  17:  
  18:     public void Add(object item)
  19:     {
  20:         m_items[index] = item;
  21:         index++;
  22:     }
  23:     public IEnumerator GetEnumerator()
  24:     {
  25:         foreach(object o in m_items)
  26:         {
  27:             // Lets check for end of list (its bad code since we used arrays)
  28:             if (o==null)
  29:             {
  30:                 break;
  31:             }
  32:             // Return the current element and then on next function call 
  33:             // resume from next element rather than starting all over again;
  34:             yield return o;
  35:         }
  36:     }
  37: }

MyArrayList範例:

   1: static void Demo6()
   2: {
   3:     MyArrayList myArrayList = new MyArrayList();
   4:  
   5:     myArrayList.Add("1");
   6:     myArrayList.Add(2);
   7:     myArrayList.Add("3");
   8:     myArrayList.Add('4');
   9:     foreach (object s in myArrayList)
  10:     {
  11:         Console.WriteLine(s);
  12:     }
  13: }

接下來,加入泛型: MyList

   1: class MyList<T> : IEnumerable<T>
   2: {   
   3:     // 儲存陣列
   4:     T[] m_Items = null;
   5:     // 陣列索引
   6:     int index = 0;
   7:     public MyList()
   8:     {
   9:         // For the sake of simplicity lets keep them as arrays
  10:         // ideally it should be link list
  11:         m_Items = new T[100];
  12:     }
  13:     public void Add(T item)
  14:     {
  15:         // Let us only worry about adding the item 
  16:         m_Items[index] = item;
  17:         index++;
  18:     }
  19:  
  20:     public IEnumerator<T> GetEnumerator()
  21:     {
  22:        foreach(T o in m_Items)
  23:         {
  24:             // Lets check for end of list (its bad code since we used arrays)
  25:             if (o==null)
  26:             {
  27:                 break;
  28:             }
  29:             // Return the current element and then on next function call 
  30:             // resume from next element rather than starting all over again;
  31:             yield return m_Items[index];
  32:         }
  33:     }
  34:  
  35:     IEnumerator IEnumerable.GetEnumerator()
  36:     {
  37:         // Lets call the generic version here
  38:         return this.GetEnumerator();
  39:     }
  40: }

MyList範例:

   1: // Let us first see how we can enumerate an custom MyList<t> class implementing IEnumerable<T>
   2:  MyList<string> myListOfStrings = new MyList<string>();
   3:  
   4:  myListOfStrings.Add("one");
   5:  myListOfStrings.Add("two");
   6:  myListOfStrings.Add("three");
   7:  myListOfStrings.Add("four");
   8:  
   9:  foreach (string s in myListOfStrings)
  10:  {
  11:      Console.WriteLine(s);
  12:  }

 

image

1. 利用while()搭配yield指令,每次呼叫GetRandIntSub()吐出一個隨機整數值,其格式為IEnumerable<int>

   1: private static IEnumerable<int> GetRandIntSub(int range)
   2: {
   3:   while(true)
   4:   {
   5:       Random r = new Random();
   6:       yield return r.Next() % range;
   7:   }
   8: }

2. 再利用foreach去接收IEnumerable<int>的東西

   1: private static int GetRandInt()
   2: {
   3:     int range = 10;
   4:     int output = 0;
   5:     foreach(int val in GetRandIntSub(range))
   6:     {     
   7:         output = val;
   8:         return output;             
   9:     }
  10:     return output;
  11: }

3. 呼叫GetRandInt()

   1: private static void Demo8()
   2: {
   3:     IList<int> myList = new List<int>();
   4:     for (int i = 0; i < 10; i++)
   5:     {
   6:         myList.Add(GetRandInt());
   7:         Console.WriteLine($"length: {myList.Count}");
   8:         Console.WriteLine($"value: {myList[myList.Count - 1]}");
   9:         Thread.Sleep(100);
  10:     }
  11: }

結果如下:

image

 

參考資料:

1. A Beginner's Tutorial on Implementing IEnumerable Interface and Understanding yield Keyword

arrow
arrow
    全站熱搜

    me1237guy 發表在 痞客邦 留言(0) 人氣()