C # contact and Analysis of IEnumerable, IEnumerator, and foreach,
1. About foreach and
Both foreach and for are cyclic keywords. You can use these two keywords to traverse the collection object and obtain the information of each object in it for operations.
static void Main(string[] args) { string[] strList = new string[] { "1","2","3","4" }; for (int i = 0; i < strList.Length; i++) { Console.WriteLine(strList[i]); } foreach (string str in strList) { Console.WriteLine(str); } Console.ReadKey(); }
The output of the above results is the same. Let's see if IL is the same.
1 IL_002c: br. s IL_003d // where for starts 2 IL_002e: nop 3 IL_002f: ldloc.0 4 IL_0030: ldloc.1 5 IL_0031: ldelem. ref 6 IL_0032: call void [mscorlib] System. console: WriteLine (string) 7 IL_0037: nop 8 IL_0038: nop 9 IL_0039: ldloc.1 // 10 IL_003a: ldc. i4.1 // 11 IL_003b: add // index plus 1. The index here is the index 12 IL_003c: IL_003d: ldloc.114 IL_003e: ldloc.015 limit: ldlen16 IL_0040: conv. i417 IL_0041: clt18 IL_0043: stloc. s CS $4 $000119 IL_0045: ldloc. s CS $4 $000120 IL_0047: brtrue. s IL_002e // jump to line 21 IL_0049: nop22 IL_004a: ldloc.023 IL_004b: stloc. s CS $6 $000224 IL_004d: ldc. i4.025 IL_004e: stloc. s CS $7 $000326 IL_0050: br. s IL_0067 // place where foreach starts 27 IL_0052: ldloc. s CS $6 $000228 IL_0054: ldloc. s CS $7 $000329 IL_0056: ldelem. ref30 IL_0057: stloc.231 IL_0058: nop32 IL_0059: ldloc.233 IL_005a: call void [mscorlib] System. console: WriteLine (string) 34 IL_005f: nop35 IL_0060: nop36 IL_0061: ldloc. s CS $7 $0003 // 37 IL_0063: ldc. i4.1 // 38 IL_0064: add // add 139 IL_0065: stloc to the current index. s CS $7 $000340 IL_0067: ldloc. s CS $7 $000341 IL_0069: ldloc. s CS $6 $000242 IL_006b: ldlen43 IL_006c: conv. i444 IL_006d: clt45 IL_006f: stloc. s CS $4 $000146 IL_0071: ldloc. s CS $4 $000147 IL_0073: brtrue. s IL_0052 // jump to 27 rows
From the IL, we can see that the index in the for loop is the index of the for itself (that is, I). During the loop process, foreach stores a value at a specified position, this value is the index used for loop. Therefore, in fact, foreach still stores an index value for loop, but we didn't notice this variable in the Process of use.
Let's take a look at the following example:
static void RunFor() { string[] strList = new string[] { "1","2","3","4" }; for (int i = 0; i < strList.Length; i++) { strList[i] = "1"; } } static void RunForeach() { string[] strList = new string[] { "1","2","3","4" }; foreach (string str in strList) { str = "1"; } }
Compilation error:"Str" is a "foreach iteration variable" and cannot be assigned a value.
static void RunFor() { List<string> strList = new List<string>() { "1","2","3","4" }; for (int i = 0; i < strList.Count; i++) { strList[i] = "1"; } } static void RunForeach() { List<string> strList = new List<string>() { "1","2","3","4" }; foreach (string str in strList) { str = "1"; } }
Similarly, the compiler gives the same error.
What if the current item is removed from foreach?
class Program { static void Main(string[] args) { List<string> strs = new List<string>() { "1", "2", "3", "4" }; foreach (string str in strs) { strs.Remove(str); } Console.ReadKey(); } }
An exception occurred during running.
It can be seen that an error occurs when you remove a variable of the IEnumerable type. Therefore, in foreach, the value of the Set object in the iteration cannot be changed.
2. Relationship between foreach and IEnumerable
For Collection types such as List and Array, you can use for and foreach to iterate them cyclically to obtain the objects in each set for operation. Foreach can be used because the List, Array, and Other types implement the IEnumerable or IEnumerable <T> interface.
public interface IEnumerable{ IEnumerator GetEnumerator();}
The IEnumerable interface has only one internal method. The GetEnumerator () method returns an IEnumerator-type object.
public interface IEnumerator{ bool MoveNext(); object Current { get; } void Reset();}
It can be seen that there are three members in the IEnumerator interface. The MoveNext function is used to move the position, which indicates the Current attribute of the Current object and resets the Reset function.
Let's take the ArrayList type as an example to see how this interface is implemented.
First, an array variable is used to store the traversal set object.
object[] _items;
Private internal classArrayListEnumeratorSimpleImplements the IEnumerator interface member.
1 public bool MoveNext () 2 {3 int num; 4 if (this. version! = This. list. _ version) 5 {6 throw new InvalidOperationException (Environment. getResourceString ("InvalidOperation_EnumFailedVersion"); 7} 8 if (this. isArrayList) 9 {10 if (this. index <(this. list. _ size-1) 11 {12 num = this. index + 1; 13 this. index = num; 14 this. currentElement = this. list. _ items [num]; // In fact, the member of the internal array variable is still 15 return true; 16} 17 this. currentElement = dummyObject; 18 this. index = this. list. _ size; 19 return false; 20} 21 if (this. index <(this. list. count-1) 22 {23 num = this. index + 1; 24 this. index = num; 25 this. currentElement = this. list [num]; // array variable member 26 return true; 27} 28 this. index = this. list. count; 29 this. currentElement = dummyObject; 30 return false; 31}
In MoveNext, an internal _ items array is iterated during the iteration loop, that is, the value obtained each time is a member of _ items, and the _ items array is the index array of ArrayList. The current index value is saved after each iteration for the next use.
It is easy to see that the internal implementation of the IEnumerator interface is the same as that of the for interface.
The following code throws an InvalidOperationException when you continue accessing the enumerated value:
if (this.version != this.list._version) { throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_EnumFailedVersion")); }
This judgment is available in Reset and MoveNext. If the enumerated value is modified, the corresponding version number will change (the function will executeThis. _ version ++So that the version number is changed, and other functions that change the enumerated value State are similar ).
3. Custom implementation iterator
Specific implementation code:
class Program { static void Main(string[] args) { TestIEnumerable test = new TestIEnumerable(); foreach (string str in test) { Console.WriteLine(str); } Console.ReadKey(); } } class TestIEnumerable : IEnumerable { private string[] _item; public TestIEnumerable() { _item = new string[] { "1","2","3","4" }; } public string this[int index] { get { return _item[index]; } } public IEnumerator GetEnumerator() { return new EnumeratorActualize(this); } class EnumeratorActualize : IEnumerator { private int index; private TestIEnumerable _testEnumerable; private object currentObj; public EnumeratorActualize(TestIEnumerable testEnumerable) { _testEnumerable = testEnumerable; currentObj = new object(); index = -1; } public object Current { get { return currentObj; } } public bool MoveNext() { if (index < _testEnumerable._item.Length - 1) { index++; currentObj = _testEnumerable._item[index]; return true; } index = _testEnumerable._item.Length; return false; } public void Reset() { index = -1; } } }