foreach内部做的一点事

  1 在C#中,foreach的使用简化了很多循环语法的书写。如果我们仅仅把foreach当成for循环的省略写法的话,就显得有点大才小用了。
  2 
  3 事实上,foreach与“迭代”和“枚举”密切相关。
  4 
  5 C#编译器会把foreach语句转换为IEnumerable接口的方法和属性。
  6 
  7 例如:
  8 
  9  foreach (var p in persons)            
 10 
 11 {
 12 
 13 Console.WriteLine(p);     
 14 
 15 }
 16 
 17 以上代码迭代persons数组中的所有元素,并逐个显示他们。
 18 
 19 foreach语句会解析成下面的代码。首先调用GetEnumerator()方法,获得数组的一个枚举。
 20 
 21 在while循环中(只要MoveNext()返回true),用Current属性访问数组中的元素:
 22 
 23 IEnumerator enumerator = persons.GetEnumerator();            
 24 
 25 while (enumerator.MoveNext())            
 26 
 27 {                
 28 
 29 Person p = (Person) enumerator.Current; Console.WriteLine(p);            
 30 
 31 }
 32 
 33 这里要说明的是。用[]声明数组是C#中使用Array类的记号。Array类实现了IEnumerable接口中的GetEnumerator()方法。
 34 
 35 所以使用foreach语句迭代数组,其实是使用了Array类中个GetEnumerator()方法。
 36 
 37 也就是说,只要是实现了IEnumerable接口中的GetEnumerator()方法的类,都可以用foreach语句来迭代。
 38 
 39 IEnumerable接口中的GetEnumerator()方法是这样定义的:
 40 
 41 IEnumerator GetEnumerator()
 42 
 43 其返回的类型是一个IEnumerator接口。
 44 
 45 IEnumerator接口中的Current属性返回光标所在的元素。
 46 
 47 IEnumerator接口中的MoveNext()方法移动到集合的下一个元素上,如果有这个元素,该方法就返回true。如果集合不再有更多的元素,该方法就返回false。
 48 
 49 IEnumerator接口中的Reset()方法将光标重新定位于集合的开头。许多枚举会抛出NotSupportedException异常。
 50 
 51 下面,我们来写一个实现了IEnumerable接口的类。
 52 
 53  public class HelloCollection:IEnumerable                    
 54 
 55 {                        
 56 
 57     public IEnumerator GetEnumerator()                        
 58 
 59    {                            
 60 
 61      yield return "Hello";                            
 62 
 63      yield return "World";                        
 64 
 65    }                    
 66 
 67 }
 68 
 69 现在可以用foreach语句迭代了:
 70 
 71 static void Main(string[] args)        
 72 
 73 {
 74 
 75          HelloCollection helloCollection=new HelloCollection ();            
 76 
 77          foreach (string s in helloCollection )            
 78 
 79          {                
 80 
 81          Console.WriteLine(s);            
 82 
 83          }        
 84 
 85 }
 86 
 87 实际上,yield return语句返回集合的一个元素,并移动到下一个元素上。它会将类HelloCollection解析成如下代码:
 88 
 89  public class HelloCollection : IEnumerable        
 90 
 91 {            
 92 
 93     public IEnumerator GetEnumerator()            
 94 
 95    {                
 96 
 97       Enumertor enumerator = new Enumerator();                
 98 
 99       return enumerator;            
100 
101    }
102 
103    public class Enumertor : IEnumerator, IDisposable            
104 
105   {                
106 
107       private int state;                
108 
109       private object current;
110 
111       public Enumertor(int state)                
112 
113      {                    
114 
115         this.state = state;                
116 
117      }            
118 
119   }                
120 
121      bool System.Collections .IEnumerator .MoveNext()                
122 
123    {                    
124 
125      switch (state)                    
126 
127        {                        
128 
129           case 0:                            
130 
131           current = "Hello";                            
132 
133           state = 1;                            
134 
135           return true;                        
136 
137           case 1:                            
138 
139           current = "World";                           
140 
141          state = 2;                            
142 
143          return true ;                        
144 
145          case 2:                            
146 
147          break ;                    
148 
149       }                    
150 
151     return false ;                
152 
153     }
154 
155   void System.Collections .IEnumerator .Reset()                
156 
157    {                    
158 
159      throw new NotSupportedException();                
160 
161    }
162 
163   object System.Collections .IEnumerator .Current                
164 
165   {                    
166 
167       get 
168 
169          {
170 
171             return current;
172 
173          }                
174 
175     }                
176 
177 void IDisposable.Dispose()                
178 
179    {                
180 
181    }        
182 
183 }
184 
185  
186 
187  
188 
189  
190 
191 foreach语句默认用GetEnumerator()方法迭代,也可以自行制定迭代方法。举例:
192 
193 public class MusicTitles:IEnumerable        
194 
195 {            
196 
197    string[] names = { "Tubular Bells", "Hergest Ridge", "Ommadawn", "Platinum" };
198 
199    public IEnumerator GetEnumerator()  /*顺序迭代*/            
200 
201    {                
202 
203      for (int i = 0; i < 4; i++)                    
204 
205      yield return names[i];            
206 
207     }
208 
209    public IEnumerator Reverse()  /*逆序迭代*/            
210 
211     {                
212 
213        for (int i = 3; i >= 0; i--)                    
214 
215        yield return names[i];            
216 
217     }        
218 
219 }
220 
221 在foreach语句中不必写明使用GetEnumerator()方法迭代,因为这是默认方法。如下:
222 
223 static void Main(string[] args)        
224 
225 {
226 
227     MusicTitles titles = new MusicTitles();            
228 
229     foreach (string title in titles)            
230 
231     {                
232 
233       Console.WriteLine(title);            
234 
235      }
236 
237     Console.WriteLine();
238 
239     foreach (string title in titles.Reverse())            
240 
241     {                
242 
243      Console.WriteLine(title);            
244 
245     }        
246 
247 }

 

posted @ 2014-06-02 11:24  与猪共舞  阅读(262)  评论(0编辑  收藏  举报