您的位置:首页 > 编程语言 > C#

[c#基础]集合foreach的必要条件和自定义集合

2014-02-14 20:14 495 查看

引言

最近翻看了之前的学习笔记,看到foreach,记得当时老师讲的时候,有点犯浑,不是很明白,这好比,上小学时,你不会乘法口诀,但是随着时间的增长,你不自觉的都会了,也悟出个小道理,有些东西,你当时不太懂,但随着你的阅历和经验的增长,有那么一天你会恍然大悟,哦,原来是这样。

自定义集合类

提到foreach就不得不说集合,那么就先从自定义的集合开始吧。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Wolfy.自定义集合
{
/// <summary>
/// 自定义集合类
/// </summary>
public class MyArrayList
{
/// <summary>
/// 集合的容量属性 成倍数增加0,4,8,且只读
/// </summary>
public int Capacity
{
get
{
return this.objArray.Length == 0 ? 0 : this.index > this.objArray.Length ? this.objArray.Length * 2 : this.objArray.Length;
}
}
/// <summary>
/// 集合实际元素个数 且只读
/// </summary>
public int Count
{
get
{
return this.index;
}
}
private int index;
private object[] objArray;
public MyArrayList()
{
index = 0;
//初始化0长度的数组
this.objArray = new object[0];
}
/// <summary>
/// 添加元素
/// </summary>
/// <param name="value">元素值</param>
public void Add(object value)
{
if (index >= this.objArray.Length)
{
object[] newArray = index == 0 ? new object[this.objArray.Length + 4] : new object[this.objArray.Length * 2];
objArray.CopyTo(newArray, 0);
objArray = newArray;
objArray[index++] = value;
}
else
{
objArray[index++] = value;
}
}
/// <summary>
/// 添加数组
/// </summary>
/// <param name="objs"></param>
public void AddRange(object[] objs)
{
for (int i = 0; i < objs.Length; i++)
{
this.Add(objs[i]);
}
}
}
}


不知道自定义的集合和ArrayList是否一样,可以简单的测试一下。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Wolfy.自定义集合
{
class Program
{
static void Main(string[] args)
{
ArrayList list = new ArrayList();
//MyArrayList list = new MyArrayList();
Console.WriteLine("count:" + list.Count);
Console.WriteLine("Capacity:" + list.Capacity);
list.Add(1);
list.Add(1);
Console.WriteLine("count:" + list.Count);
Console.WriteLine("Capacity:" + list.Capacity);
list.Add(1);
list.Add(1);
list.Add(1);
Console.WriteLine("count:" + list.Count);
Console.WriteLine("Capacity:" + list.Capacity);
list.Add(1);
list.Add(1);
list.Add(1);
list.Add(1);
list.Add(1);
list.Add(1);
Console.WriteLine("count:" + list.Count);
Console.WriteLine("Capacity:" + list.Capacity);
object[] arr = { 1, 2, 3, 4, 5, 6 };
list.AddRange(arr);
Console.WriteLine("count:" + list.Count);
Console.WriteLine("Capacity:" + list.Capacity);
Console.Read();
}
}
}


此时是.Net中的ArrayList,结果:



自定义的集合,结果:



输出结果一样,那么现在用foreach遍历,自定义集合中的元素。F6编译,会提示错误。



foreach语句

其实foreach是怎样工作的呢?

众所周知foreach中in后面的对象应该是实现IEnumerable接口的,程序运行时本质是在调用IEnumerable的GetEnumerator函数来返回一个IEnumerator对象,foreach就是利用IEnumerator对象的Current,MoveNext和Reset成员来进行一段数据的枚举。简单的代码实现如下:

//System.Collections下的IEnumerator
IEnumerator enumerator = this.objArray.GetEnumerator();
while (enumerator.MoveNext())
{
Console.WriteLine(enumerator.Current);
}


将这个代码放在自定义集合中,定义一个方法GetArray(),然后测试一下

/// <summary>
/// 得到所有的元素
/// </summary>
public void GetArray()
{
//System.Collections下的IEnumerator
IEnumerator enumerator = this.objArray.GetEnumerator();
while (enumerator.MoveNext())
{
Console.Write(enumerator.Current+“,”);
}
}


测试结果:



你运行会发现多出很多逗号,原因是执行后,enumerator没有被Dispose掉,而继承IDisposable的迭代器(IEnumerator)在foreach结束后会被正确处理掉(调用Dispose方法)。

自定义集合实现IEnumerable接口

实现IEnumerable接口必须实现它里面的成员GetEnumerator()方法:

public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}


该方法的返回值为实现了IEnumerator接口的类的对象。那么现在需要定义一个实现了该接口的类。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Wolfy.自定义集合
{
public class MyEnumerator : IEnumerator
{
/// <summary>
/// 返回当前指针指向的元素的值
/// </summary>
public object Current
{
get { throw new NotImplementedException(); }
}
/// <summary>
/// 将指针向前移动1位,并判断当前位有没有元素.指针默认在-1位置
/// </summary>
/// <returns></returns>
public bool MoveNext()
{
throw new NotImplementedException();
}
/// <summary>
/// 重置
/// </summary>
public void Reset()
{
throw new NotImplementedException();
}
}
}


迭代需要数组参数,在构造函数中将自定义集合中的数组传进来,并且在MoveNext中需要判断指针是否移动到数组的末尾,那么需要数组的长度。
MyArrayList中GetEnumerator()方法实现

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


MyEnumerator类

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Wolfy.自定义集合
{
public class MyEnumerator : IEnumerator
{
/// <summary>
/// 指针的默认位置
/// </summary>
private int index = -1;
private object[] objArray;
/// <summary>
/// 数组长度
/// </summary>
private int count;
public MyEnumerator(object[] objArray, int count)
{

this.objArray = objArray;
this.count = count;
}
/// <summary>
/// 返回当前指针指向的元素的值
/// </summary>
public object Current
{
get { return this.objArray[index]; }
}
/// <summary>
/// 将指针向前移动1位,并判断当前位有没有元素.指针默认在-1位置
/// </summary>
/// <returns></returns>
public bool MoveNext()
{
index++;//指针首先向前移动一位
if (index < this.count)
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 重置
/// </summary>
public void Reset()
{
index = -1;
}
}
}


测试,foreach语句,然后编译不再报错,说明已经成功了,结果如下:



这次后边不会多出逗号,原因实现了迭代器接口而迭代器继承自IDisposable接口,最后调用了Dispose()方法

代码下载,请戳这里:http://pan.baidu.com/s/1pJsGyHt

总结

foreach遍历in后面的对象需实现IEnumerable接口。

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

东西比较基础,以上是个人理解,如理解有误,请指正,以免误人子弟。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐