After the foreach is translated into IL, the actual code is as follows:
That is, foreach actually invokes the GetEnumerator method of an enumerable object, obtains a enumerator object, and then executes a while loop on the enumerator, and then gets each value in the enumerable object.
You can think of all the values in an enumerable object as a linked list, enumerator is a pointer to the list, enumerator.current is the element that is currently pointing, and Enumerator.movenext is the pointer behind. You can then use the while loop to get all the values in the object in a way that is similar to traversing the list.
An enumerable object that itself must implement the IEnumerable interface (where there is only one GetEnumerator method).
In the GetEnumerator method, you can directly return a enumerator object for enumeration.
You can also return all values that need to be enumerated with multiple yield returns, where the yield statement creates an object that implements the IEnumerator interface.
It is important to note that if you use yield return in a method, you cannot use normal return, and if yield return is used, the code in the method body will not run at the time of the call, only after the enumeration has started (call Enumerator.movenext ()) Before it starts to run. And each enumeration will only run to the next yield return.
classtest:ienumerable { Public Static intj =0; Public Static stringSS ="begin"; Public inti =0; Publicienumerable<string>A () {TEST.SS+="[email protected]"; //string[] aaaa = {"1", "2", "3"}; //return AAAA. AsEnumerable ();TEST.J++; yield returntest.j.tostring (); TEST.SS+="[email protected]"; TEST.J++; yield returntest.j.tostring (); TEST.J++; yield returntest.j.tostring (); } PublicIEnumerator GetEnumerator () {TEST.SS+="[email protected]"; string[] aaaa = {"1","2","3" }; returnAAAA. GetEnumerator (); //test.j++; //yield return test.j.tostring (); //test.j++; //yield return test.j.tostring (); //test.j++; //yield return test.j.tostring (); } } classProgram {Static voidMain (string[] args) {Console.WriteLine (TEST.SS); Test T=Newtest (); Console.WriteLine (TEST.SS); varTB =T.getenumerator (); Console.WriteLine (TEST.SS); varA =t.a (); Console.WriteLine (TEST.SS); varb =A.getenumerator (); //var c = b.current; //B.movenext (); //Console.WriteLine (c); //B.movenext ();Console.WriteLine (TEST.SS); inti =Ten; foreach(varSincht) {i++; //TEST.J = i;Console.WriteLine (TEST.J +" "+s); } Console.WriteLine (TEST.SS); ReadLine (); } }
As in the code above, the method GetEnumerator uses a normal return, at which time it runs to
var TB = t.getenumerator ();
The output is: [Email protected]
Because T was called. GetEnumerator (); will run test.ss + = "[email protected]"; So the output is [email protected]
But continue to run down, whether it's calling test.a () or TEST.A (). GetEnumerator () TEST.SS value has not changed, that is, no implementation TEST.SS + = "[email protected]" ; That is, the code in the method test.a has not been executed at this time.
The code in the Test.a method body is not started until after foreach begins, that is, the enumeration begins (called MoveNext).
In other words, the method that contains yield return is not a normal method, calling the method in normal invocation mode, is not executing any code inside, until the Enumerator.movenext method is called.
And each time the Enumerator.movenext method is called, the code executes only to the next yield return, and the result returned by that yield return is the value of this enumeration.
C#yield,ienumerable,ienumerator