Objective
In the previous article I concluded that the value of the item in the iterate iterator modification iterator did not take effect because yield return was used, and each iteration of the iterator executed the method that returned the iterator. This article is followed by the previous article, which verifies the cause of this situation from the point of view of the code implementation.
Let's start with the next way to look at the code implementation: using Reflector to decompile the DLL or EXE file, we can see the code inside, and in the following configuration you can choose the C # version of the code implementation:
Tools->options
Here we choose to None, then reflector will not be the anti-compilation code optimization, the most primitive implementation method to show us, we can see in this code a lot of C # relative to the bottom of the implementation.
Body
Here is the code we want to look at, one that gets an example of the iterator returning multiple listtest classes, and the other is iterating through the iterator and modifying the contents of the iterator:
public void Yieldtest () {var list = getenumerable (); for (int i = 0; i <, i++) {foreach (var test in list) {test.atr1 = 0; TEST.ATR2 = "11"; }}}public ienumerable<listtest> getenumerable () {for (int i = 0; i < 2; i++) {yield return new Listtest () {atr1 = i + 1, atr2 = string. Format ("test{0}", I + 1)}; }}
public void Yieldtest () {ienumerable<listtest> enumerable; int num; Listtest test; Ienumerator<listtest> Enumerator; BOOL Flag; Enumerable = this. Getenumerable (); num = 0; Goto label_005a; Label_000c:enumerator = Enumerable. GetEnumerator (); label_0015:try {goto label_0034; Label_0017:test = Enumerator. Current; TEST.ATR1 = 0; TEST.ATR2 = "11"; Label_0034:if (Enumerator. MoveNext ()! = null) {goto label_0017; } goto label_0054; } finally {label_0042:if ((enumerator = = NULL)! = NULL) {goto label_0053; } enumerator. Dispose (); label_0053:; }label_0054:num + = 1; Label_005a:if (num <)! = null) {goto label_000c; } return; public void Yieldtest () {ienumerable<listtest> enumerable; int num; Listtest test; Ienumerator<listtest> Enumerator; BOOL Flag; Enumerable = this. Getenumerable (); num = 0; Goto label_005a; Label_000c:enumerator = Enumerable. GetEnumerator (); label_0015:try {goto label_0034; Label_0017:test = Enumerator. Current; TEST.ATR1 = 0; TEST.ATR2 = "11"; Label_0034:if (Enumerator. MoveNext ()! = null) {goto label_0017; } goto label_0054; } finally {label_0042:if ((enumerator = = NULL)! = NULL) {goto label_0053; } enumerator. Dispose (); label_0053:; }label_0054:num + = 1; Label_005a:if (num <)! = null) {goto label_000c; } return; Public ienumerable<listtest> getenumerable () {<getenumerable>d__12 d__; Ienumerable<listtest> Enumerable; d__ = new <getenumerable>d__10 (-2); D__.<>4__this = this; enumerable = d__; Label_0013:return Enumerable;}
First we look at the code of the yieldtest function, a lot longer, in fact, to clarify the inside Goto statement, the logic is still very clear, here we see the following points:
- for loop is implemented by judging the stepping value num and using the goto statement.
- foreach keyword is implemented by using the current property of the iterator to take an action on the active item, and then calling the MoveNext () method to make the The property points to the next item, and then the goto statement loops through.
look at the Getenumerable () method, it's strange here, the code returns an instance of the <getenumerable>d__10 class, and there's no code logic in my function, And I don't have this class in my code, and this class is automatically generated for us, and it implements the iterator interface:
uses this iterator in the yieldtest function, and the iterator's current The property is the Listtest class returned in our code, and the logic of my code is actually in the MoveNext () method:
private bool MoveNext () {BOOL flag; int num; BOOL Flag2; num = this.<>1__state; Switch (num) {case 0:goto label_0019; Case 1:goto label_0017; } goto label_001b; Label_0017:goto label_008b; Label_0019:goto label_0020; Label_001b:goto LABEL_00AF; Label_0020:this.<>1__state =-1; This.<i>5__11 = 0; Goto LABEL_00A1; Label_0031:this.<>g__initlocalf = new Listtest (); THIS.<>G__INITLOCALF.ATR1 = This.<i>5__11 + 1; THIS.<>G__INITLOCALF.ATR2 = string. Format ("Test{0}", (int) (This.<i>5__11 + 1)); This.<>2__current = this.<>g__initlocalf; This.<>1__state = 1; flag = 1; Goto LABEL_00B3; Label_008b:this.<>1__state =-1; This.<i>5__11 + = 1; Label_00a1:if ((This.<i>5__11 < 2) = null) {goto label_0031; }label_00af:flag = 0; Label_00b3:return Flag;}
Here we can understand the two questions at the beginning of this article:
1. When you use yield return, modifying the contents of the iterator in foreach does not take effect:
Invoking the method of yield return simply returns an instance of an iterator and does not actually execute the logic in the method, and when our loop iterator calls the MoveNext () method, it will actually execute our code logic, and each loop iterator will execute MoveNext () Method gets a new instance, so each operation does not affect the next loop.
2. Each loop iterator executes the getenumerable () function:
Because the MoveNext () method is executed each time, the code in the original Getenumerable () is already in the MoveNext () method.
Here are some of my thoughts on yield :
There is no need to use yield in terms of normal demand, and some unexpected effects will take us to the pits; I think the more useful use is: when the multi-threaded batch processing, get to a data call threading, while processing side to get new data, Relative to getting all the data in the assignment to threading is a situation that can improve performance, especially when it takes time to get the data.
My level is limited, the ability is general, hope can learn more knowledge, what mistake place welcome to point out!
?
C # uses Ienumerable,yield to return results, while using foreach, modifying the value of a variable within a loop is not valid (ii)