[c#基礎]集合foreach的必要條件和自定義集合


引言

最近翻看了之前的學習筆記,看到foreach,記得當時老師講的時候,有點犯渾,不是很明白,這好比,上小學時,你不會乘法口訣,但是隨着時間的增長,你不自覺的都會了,也悟出個小道理,有些東西,你當時不太懂,但隨着你的閱歷和經驗的增長,有那么一天你會恍然大悟,哦,原來是這樣。

自定義集合類

提到foreach就不得不說集合,那么就先從自定義的集合開始吧。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Wolfy.自定義集合
 8 {
 9     /// <summary>
10     /// 自定義集合類
11     /// </summary>
12     public class MyArrayList
13     {
14         /// <summary>
15         /// 集合的容量屬性 成倍數增加0,4,8,且只讀
16         /// </summary>
17         public int Capacity
18         {
19             get
20             {
21                 return this.objArray.Length == 0 ? 0 : this.index > this.objArray.Length ? this.objArray.Length * 2 : this.objArray.Length;
22             }
23         }
24         /// <summary>
25         /// 集合實際元素個數 且只讀
26         /// </summary>
27         public int Count
28         {
29             get
30             {
31                 return this.index;
32             }
33         }
34         private int index;
35         private object[] objArray;
36         public MyArrayList()
37         {
38             index = 0;
39             //初始化0長度的數組
40             this.objArray = new object[0];
41         }
42         /// <summary>
43         /// 添加元素
44         /// </summary>
45         /// <param name="value">元素值</param>
46         public void Add(object value)
47         {
48             if (index >= this.objArray.Length)
49             {
50                 object[] newArray = index == 0 ? new object[this.objArray.Length + 4] : new object[this.objArray.Length * 2];
51                 objArray.CopyTo(newArray, 0);
52                 objArray = newArray;
53                 objArray[index++] = value;
54             }
55             else
56             {
57                 objArray[index++] = value;
58             }
59         }
60         /// <summary>
61         /// 添加數組
62         /// </summary>
63         /// <param name="objs"></param>
64         public void AddRange(object[] objs)
65         {
66             for (int i = 0; i < objs.Length; i++)
67             {
68                 this.Add(objs[i]);
69             }
70         }
71     }
72 }

不知道自定義的集合和ArrayList是否一樣,可以簡單的測試一下。

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Wolfy.自定義集合
 9 {
10     class Program
11     {
12         static void Main(string[] args)
13         {
14             ArrayList list = new ArrayList();
15             //MyArrayList list = new MyArrayList();
16             Console.WriteLine("count:" + list.Count);
17             Console.WriteLine("Capacity:" + list.Capacity);
18             list.Add(1);
19             list.Add(1);
20             Console.WriteLine("count:" + list.Count);
21             Console.WriteLine("Capacity:" + list.Capacity);
22             list.Add(1);
23             list.Add(1);
24             list.Add(1);
25             Console.WriteLine("count:" + list.Count);
26             Console.WriteLine("Capacity:" + list.Capacity);
27             list.Add(1);
28             list.Add(1);
29             list.Add(1);
30             list.Add(1);
31             list.Add(1);
32             list.Add(1);
33             Console.WriteLine("count:" + list.Count);
34             Console.WriteLine("Capacity:" + list.Capacity);
35             object[] arr = { 1, 2, 3, 4, 5, 6 };
36             list.AddRange(arr);
37             Console.WriteLine("count:" + list.Count);
38             Console.WriteLine("Capacity:" + list.Capacity);
39             Console.Read();
40         }
41     }
42 }

此時是.Net中的ArrayList,結果:

自定義的集合,結果:

輸出結果一樣,那么現在用foreach遍歷,自定義集合中的元素。F6編譯,會提示錯誤。

foreach語句

 其實foreach是怎樣工作的呢?

眾所周知foreach中in后面的對象應該是實現IEnumerable接口的,程序運行時本質是在調用IEnumerable的GetEnumerator函數來返回一個IEnumerator對象,foreach就是利用IEnumerator對象的Current,MoveNext和Reset成員來進行一段數據的枚舉。簡單的代碼實現如下:

1             //System.Collections下的IEnumerator
2             IEnumerator enumerator = this.objArray.GetEnumerator();
3             while (enumerator.MoveNext())
4             {
5                 Console.WriteLine(enumerator.Current);
6             }

將這個代碼放在自定義集合中,定義一個方法GetArray(),然后測試一下

 1         /// <summary>
 2         /// 得到所有的元素
 3         /// </summary>
 4         public void GetArray()
 5         {
 6             //System.Collections下的IEnumerator
 7             IEnumerator enumerator = this.objArray.GetEnumerator();
 8             while (enumerator.MoveNext())
 9             {
10                 Console.Write(enumerator.Current+“,”);
11             }
12         }

測試結果:

你運行會發現多出很多逗號,原因是執行后,enumerator沒有被Dispose掉,而繼承IDisposable的迭代器(IEnumerator)在foreach結束后會被正確處理掉(調用Dispose方法)。

自定義集合實現IEnumerable接口

實現IEnumerable接口必須實現它里面的成員GetEnumerator()方法:

1         public IEnumerator GetEnumerator()
2         {
3             throw new NotImplementedException();
4         }

該方法的返回值為實現了IEnumerator接口的類的對象。那么現在需要定義一個實現了該接口的類。

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Wolfy.自定義集合
 9 {
10     public class MyEnumerator : IEnumerator
11     {
12         /// <summary>
13         /// 返回當前指針指向的元素的值
14         /// </summary>
15         public object Current
16         {
17             get { throw new NotImplementedException(); }
18         }
19         /// <summary>
20         /// 將指針向前移動1位,並判斷當前位有沒有元素.指針默認在-1位置
21         /// </summary>
22         /// <returns></returns>
23         public bool MoveNext()
24         {
25             throw new NotImplementedException();
26         }
27         /// <summary>
28         /// 重置
29         /// </summary>
30         public void Reset()
31         {
32             throw new NotImplementedException();
33         }
34     }
35 }

迭代需要數組參數,在構造函數中將自定義集合中的數組傳進來,並且在MoveNext中需要判斷指針是否移動到數組的末尾,那么需要數組的長度。
MyArrayList中GetEnumerator()方法實現

1         public IEnumerator GetEnumerator()
2         {
3             return new MyEnumerator(this.objArray, this.Count);
4         }

MyEnumerator類

 1 using System;
 2 using System.Collections;
 3 using System.Collections.Generic;
 4 using System.Linq;
 5 using System.Text;
 6 using System.Threading.Tasks;
 7 
 8 namespace Wolfy.自定義集合
 9 {
10     public class MyEnumerator : IEnumerator
11     {
12         /// <summary>
13         /// 指針的默認位置
14         /// </summary>
15         private int index = -1;
16         private object[] objArray;
17         /// <summary>
18         /// 數組長度
19         /// </summary>
20         private int count;
21         public MyEnumerator(object[] objArray, int count)
22         {
23 
24             this.objArray = objArray;
25             this.count = count;
26         }
27         /// <summary>
28         /// 返回當前指針指向的元素的值
29         /// </summary>
30         public object Current
31         {
32             get { return this.objArray[index]; }
33         }
34         /// <summary>
35         /// 將指針向前移動1位,並判斷當前位有沒有元素.指針默認在-1位置
36         /// </summary>
37         /// <returns></returns>
38         public bool MoveNext()
39         {
40             index++;//指針首先向前移動一位
41             if (index < this.count)
42             {
43                 return true;
44             }
45             else
46             {
47                 return false;
48             }
49         }
50         /// <summary>
51         /// 重置
52         /// </summary>
53         public void Reset()
54         {
55             index = -1;
56         }
57     }
58 }

測試,foreach語句,然后編譯不再報錯,說明已經成功了,結果如下:

這次后邊不會多出逗號,原因實現了迭代器接口而迭代器繼承自IDisposable接口,最后調用了Dispose()方法

代碼下載,請戳這里:http://pan.baidu.com/s/1pJsGyHt

總結

foreach遍歷in后面的對象需實現IEnumerable接口。

迭代器概念可參考:http://msdn.microsoft.com/zh-cn/library/dscyy5s0(VS.80).aspx

東西比較基礎,以上是個人理解,如理解有誤,請指正,以免誤人子弟。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM