foreach适合在什么情况下使用+foreach的内部原理剖析
先看下面这段代码;foreach是可以遍历的。没有问题!
1 ArrayList arrList1 = new ArrayList() { 20, 3, 49, 39, 48 }; 2 foreach (var item in arrList1) 3 { 4 Console.WriteLine(item); 5 }
这个也没有问题,可以正常的遍历
1 string[] names = { "a", "b", "c", "d" }; 2 foreach (string item in names) 3 { 4 Console.WriteLine(item); 5 }
我们自己模拟一个类似于数组的对象
1 public class Person : IEnumerable 2 { 3 private List<string> listCar = new List<string>(); 4 5 public int Count 6 { 7 get 8 { 9 return this.listCar.Count; 10 } 11 12 } 13 14 public string this[int index] 15 { 16 get 17 { 18 return listCar[index]; 19 } 20 21 set 22 { 23 if (index >= listCar.Count) 24 { 25 listCar.Add(value); 26 } 27 else 28 { 29 listCar[index] = value; 30 } 31 } 32 } 33 }
好,这个时候我们来实例化这个person对象
1 Person p = new Person(); 2 p[0] = "奇瑞QQ"; 3 p[1] = "infiniti"; 4 p[2] = "阿斯顿马丁"; 5 for (int i = 0; i < p.Count; i++) 6 { 7 Console.WriteLine(p[i]); 8 }
这个时候,也没有问题。能打印出结果!
这个时候我们用foreach来遍历试试看
1 foreach (string item in p) 2 { 3 Console.WriteLine(item); 4 }
这个时候编译报错,提示不包含 “GetEnumerator”的公共定义
这个GetEnumerator是什么呢?
我们打开反编译工具:分别查找ArrayList,string,List 这几个类,你会惊奇的发现他们都具备这个方法!那我们就可以大胆的猜测是不是只要实现了这个GetEnumerator()方法就能实现foreach遍历呢;
这个时候我们在上面的Person类中也实现这个样的一个方法;实现有两种方法
方法一:直接在末尾加上这个方法
方法二:实现IEnumerable接口
public class Person : IEnumerable { private List<string> listCar = new List<string>(); public int Count { get { return this.listCar.Count; } } public string this[int index] { get { return listCar[index]; } set { if (index >= listCar.Count) { listCar.Add(value); } else { listCar[index] = value; } } } public string Name { get; set; } #region IEnumerable 成员 //这个方法的作用不是用来遍历的,而是用来获取一个对象 //这个对象才是用来遍历的。 public IEnumerator GetEnumerator() { return new NotImplementedException(); } #endregion }
这个时候我们编译一下,发现不编译通过了。那是不是意味着我们就可以用foreach遍历了呢?
运行下,抛出一个异常;
public IEnumerator GetEnumerator() { return new NotImplementedException(); }
你会发现这个方法里面什么都没有,肯定是火抛出异常的!
从这里我们总结下;任何类型,只要想使用foreach来循环遍历,就必须在当前类型中存在: public IEnumerator GetEnumerator()方法,(一般情况我们会通过实现IEnumerable接口,来创建该方法。)
我们观察可以发现GetEnumerator的返回类型是IEnumerator ,我们猜测这个方法的作用不是用来遍历的,,而是用来获取一个对象。由这个对象内部来实现遍历
我们在定义一个名为PersonEnumerator的类,并且实现IEnumerator 接口,这时你会惊奇的发现,里面提供了三个方法Current(),MoveNext(),Reset();这里实现下接口就能实现,就不在粘代码了
直接把写好的拿上来
1 //这个类型,的作用就是用来遍历Person中的List集合的。 2 public class PersonEnumerator : IEnumerator 3 { 4 public PersonEnumerator(List<string> _cars) 5 { 6 cars = _cars; 7 } 8 9 //这个字段中存储的就是Person对象中的listCar集合 10 private List<string> cars; 11 12 13 //假设一开始遍历的对象的索引是-1 14 private int index = -1; 15 16 #region IEnumerator 成员 17 18 19 //表示获取当前正在遍历的那个对象 20 public object Current 21 { 22 get 23 { 24 if (index < 0) 25 { 26 return null; 27 } 28 return cars[index]; 29 } 30 } 31 //让自定义下标index累加 32 public bool MoveNext() 33 { 34 index = index + 1; 35 if (index >= cars.Count) 36 { 37 return false; 38 } 39 else 40 { 41 return true; 42 } 43 } 44 45 public void Reset() 46 { 47 index = -1; 48 } 49 50 #endregion 51 }
并且修改Person里面的GetEnumerator方法
1 public IEnumerator GetEnumerator() 2 { 3 return new PersonEnumerator(listCar); 4 }
这个时候我们在运行遍历下,这个时候已经可以使用foreach遍历了;想看内部怎么调用方法实现的吗?
1 IEnumerator etor = p.GetEnumerator(); 2 while (etor.MoveNext()) 3 { 4 string str = etor.Current.ToString(); 5 Console.WriteLine(str); 6 } 7 Console.ReadKey();