[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

东西比较基础,以上是个人理解,如理解有误,请指正,以免误人子弟。

posted @ 2014-02-14 20:14  wolfy  阅读(3389)  评论(0编辑  收藏  举报