浅析foreach原理

      在日常开发工作中,我们发现很多对象都能通过foreach来遍历,比如HashTable、Dictionary、数组等数据类型。那为何这些对象能通过foreach来遍历呢?如果写一个普通的Person类,也希望它能通过foreach来遍历应该怎么做?通过查看,发现HashTable、Dictionary、数组等数据类型都实现了一个叫IEnumerable(或其泛型版本)的接口。现在也来尝试下,让Person类实现这个接口(其实实不实现IEnumerable接口不是必须的,只要类型中有public IEnumerator GetEnumerator()这个方法即可):

 1 class Person:IEnumerable
 2     {
 3         public string[] _Name = new string[] { "sk", "jk", "yzk","wcw","ml" };
 4         public string Name { get; set; }
 5         public int Age { get; set; }
 6 
 7         public IEnumerator GetEnumerator()
 8         {
 9             return new PersonEnumerator(_Name);
10         }
11     }

可以看到GetEnumerator()方法需要一个返回值类型为实现了IEnumerator的类型,那就写个类,让其实现IEnumerator接口:

 1 class PersonEnumerator:IEnumerator
 2     {
 3         public PersonEnumerator(string[] name)
 4         {
 5             this._names = name;
 6         }
 7 
 8         public string[] _names { get; set; }
 9         public int Index = -1;
10 
11         public object Current
12         {
13             get { return _names[Index]; }
14         }
15 
16         public bool MoveNext()
17         {
18             Index++;
19             if (Index>=_names.Length)
20             {
21                 return false;
22             }
23             else
24             {
25                 return true;
26             }
27         }
28 
29         public void Reset()
30         {
31             Index = -1;
32         }
33     }

此时再运行程序,就发现Person类可以遍历了。运行结果如下:

总结,一种类型要想通过foreach遍历,其内部必须有public IEnumerator GetEnumerator()这个方法,而通常的做法是让类型实现IEnumerable接口

上面的代码还有一个问题,就是Person对象通过foreach遍历时,var并没有推断出是string类型而是object类型,这是因为Current就是object导致的,解决的方案就是泛型,看下面的代码:

 1 class Person
 2     {
 3         public string Name { get; set; }
 4         public int Age { get; set; }
 5         public string[] _Name = new string[] { "zxh", "jk", "ml", "wcw", "sk", "yzk","lmn" };
 6 
 7         public IEnumerator<string> GetEnumerator()
 8         {
 9             return new MyClass<string>(_Name);
10         }
11     }
12 
13     class MyClass<T>:IEnumerator<T>
14     {
15         public MyClass(T[] _Names)
16         {
17             this.names = _Names;
18         }
19         public T Current
20         {
21             get { return names[Index]; }
22         }
23 
24         private T[] names { get; set; }
25         private int Index = -1;
26 
27         public void Dispose()
28         {
29             //throw new NotImplementedException();
30         }
31 
32         object IEnumerator.Current
33         {
34             get { throw new NotImplementedException(); }
35         }
36 
37         public bool MoveNext()
38         {
39             Index++;
40             if (Index>=names.Length)
41             {
42                 return false;
43             }
44             else
45             {
46                 return true;
47             }
48         }
49 
50         public void Reset()
51         {
52             Index = -1;
53         }
54     }

使用的方法如下:

1            Person p1 = new Person();
2             foreach (var item in p1)
3             {
4                 Console.WriteLine(item);
5             }

可以看到var已经被推断成string.

 

下面看一下,自己写的foreach遍历,还是那个Person类(必须有IEnumerator GetEnumerator()这个方法):

把foreach遍历换成下面的代码:

1            Person p1 = new Person();
2             IEnumerator enumer = p1.GetEnumerator();
3             while (enumer.MoveNext())
4             {
5                 Console.WriteLine(enumer.Current);
6             }

上面的代码也是能够正常执行的。

扩展:

1             List<string> strLst = new List<string>();   //查看定义得知,其实现了IEnumerable<T>和IEnumerable2个接口
2             strLst.AddRange(new string[] { "sk", "jk", "yzk" });
3             IEnumerator enumer = strLst.GetEnumerator();
4             while (enumer.MoveNext())
5             {
6                 Console.WriteLine(enumer.Current);
7             }

一般没人会这么写,遍历一个对象还是直接写foreach。这只不过是其原理。

 1             Dictionary<int, char> dic = new Dictionary<int, char>();
 2             dic.Add(1, '');
 3             dic.Add(2, '');
 4             dic.Add(3, '');
 5             dic.Add(4, '');
 6             dic.Add(5, '');
 7             dic.Add(6, '');
 8             dic.Add(7,'');
 9             dic.Add(8,'');
10             dic.Add(9,'');
11 
12             IEnumerator iEnumer = dic.GetEnumerator();
13             while (iEnumer.MoveNext())
14             {
15                 KeyValuePair<int, char> item = (KeyValuePair<int, char>)iEnumer.Current;
16                 Console.WriteLine(item.Key+"   "+item.Value);
17             }

posted on 2014-07-22 15:13  chens2865  阅读(162)  评论(0编辑  收藏  举报

导航