A long time ago, I heard that for and foreach are different (not just syntax), and I have seen many articles on the Internet.
However, from the code written by myself, it is difficult to see the difference, because most of the time, for or foreach is used.
Traverse an array class.
One day I suddenly wanted to figure out this problem, so I analyzed it a little and looked at the code below:
Public void ()
{
String [] array = new string [] {"111", "222", "333 "};
For (INT I = 0; I <array. length; I ++)
{
Console. writeline (array [I]);
}
}
//// Results:
111
222
333
Public void foreachonarray ()
{
String [] array = new string [] {"111", "222", "333 "};
Foreach (string s in array)
{
Console. writeline (s );
}
}
//// Results:
111
222
333
Same input result! Let's see what ildasm tells us:
For:
Foreach:
Strange, although the called commands are not different, they are also similar. They all cycle data based on length. (It's a little different from what I said on the Internet)
(The main difference here is that the index + 1 operation is automatically performed on the array in foreach to loop, while the for operation is controlled by its own code .)
Add some code to try to change the operation value in the Loop:
Array = new string [] {"AAA", "BBB", "CCC "};
/// For () Result:
111
Bbb
CCC
/// Foreachonarray () Result:
111
222
333
It seems that the changes to the source do not take effect even in the internal loop of foreach!
If you try to change the value in the array of the current operation:
For ():
Array [I] = "changed"; // OK
Foreachonarray ():
S = "changed"; // compile error, prompt: "cannot assign to's 'because it is read-only"
If changed:
Foreachonarray ():
Array [2] = "changed"; // The changes take effect only when the foreach () loop exists.
Well, the difference is obvious,
Conclusion: do not change the operation source in the foreach (...) loop,
In the for (...) loop, it doesn't matter (it seems that the loop of for is more similar to do. While, the core is just judgment)
But is that all true?
No, to consider the loop we often make on datarowcollection (that is, datatable. datarow)-it is not an array,
According to the MS reference, only the class that implements the ienumerable interface can be used to loop on foreach (in fact, system. array also implements the ienumerable interface)
Well, now let alone arrays to create a loop on ienumerable. First, write the following Class E that implements the ienumberable interface:
Public Class E: ienumerable
{
Private innerenumerator inner;
Public E (string [] array)
{
This. Inner = new innerenumerator (array );
}
# Region ienumerable members
Public ienumerator getenumerator ()
{
Return this. Inner;
}
# Endregion
Private class innerenumerator: ienumerator, idisposable
{
Private string [] S;
Private int currentindex;
Public innerenumerator (string [] array)
{
This. S = array;
This. Reset ();
}
# Region ienumerator members
// Reset index to original
Public void reset ()
{
This. currentindex = S. Length-1;
}
// Get current object inner
Public object current
{
Get
{
Object o = This. s [This. currentindex];
This. currentindex --;
Return O;
}
}
// Is there has any other object in the array?
Public bool movenext ()
{
If (this. currentindex <0)
{
Return false;
}
Return true;
}
# Endregion
# Region idisposable members
// Dispose here
Public void dispose ()
{
Console. writeline ("dispose here! ");
}
# Endregion
}
}
Next, take this class for testing, and perform a loop above to see: (the process is roughly the same as for () and foreachonarray)
Public void foreachonienumerable ()
{
String [] array = new string [] {"111", "222", "333 "};
E = new E (array );
Foreach (string s in E)
{
Console. writeline (s );
}
}
// Result:
333
222
111
Dispose here!
The difference has occurred. This time, the dispose method is automatically called in reverse order!
(Note: We have no code to call the dispose () method !)
Use ildasm to check the actual il code:
Sure enough, there are a lot more different things.
Try
{
}
Finally
{
}
Block
Analysis:
Sequence:
1. Call the E. getenumerator () method to obtain an enumerator instance.
Equivalent to code:
Ienumerator = E. getenumertor ();
2. Call the getcurrent () method on this enumerator instance to kill the obtained object through castclass.
Convert to the string type defined in the Code
It is equivalent to the Code:
String S = (string) ienumerator. getcurrent ();
(Note: If the conversion fails, an invalidcastexception is thrown, indicating that the conversion fails and the type does not match)
(That is, if we change it to foreach (INT s in array), a runtime error will occur and an invalidcastexception will be thrown,
This is the role of the castclass keyword)
3. Perform the desired processing on the object obtained by getcurrent ().
Here is our own code:
Console. writeline (s );
4. Call movenext () on the obtained ienumerator. If it is true, it enters the next loop (jump to 2). If it is false, it jumps out of the loop.
Equivalent to code:
If (ienumerator. movenext ())
{
Goto step-2;
}
Else
{
Goto finally;
}
Finally block code:
1. Determine whether the previously obtained ienumerator has implemented the idisposable interface. If it is true, continue and if it is false, break;
And so on:
If (! (Ienumerator is idispose ))
{
Break; // over whole
}
2. Call the dispose () method on the idisposable (this is why the last sentence "dispose here! )
Because the method we wrote in dispose () is automatically called here !!!
Equivalent to code:
Ienumerator. Dispose ()
Analysis:
...
This is different from the previous loop (foreachonarray () on the array. It is a completely different code!
The conclusion is as follows:
1. The for loop does not depend on arrays or other forms of group data structure, but is simple.
After the code is called, make a judgment to determine whether to continue.
(Very similar to do... while and while loops -- here we do not analyze them in detail ~~)
2. If the foreach loop acts on an array based on the system. array type, the compiler will automatically optimize it to be very similar to the for loop.
Code, but there is a slight difference in the calls, and the check (including the compilation phase and runtime) will be much stricter than
3. The foreach loop applies to a non-system. array type (and must be a class that implements the ienumerable interface ).
The ienumerable. getenumerator () method obtains an enumertor instance, and then calls
Getcurrent () and movenext () methods, and finally determine if the enumertor instance implements the idispose interface, it will automatically call
Idispose. Dispose () method!
Then we should use for and foreach in those places respectively.
Suggestion:
1. When assigning values to the cyclic Ontology (system. array), try not to use foreach ().
2. foreach is more flexible than. (You can write your own code in movenext () and getcurrent ).
If a self-compiled class implements the ienumerable interface, you can use the foreach loop, regardless of whether there is a real array inside it,
You can also customize cycle rules.
3. From the OO principle, the foreach loop is more suitable for most cases.
(In fact, foreach's implementation is a typical iterator mode. The following describes its advantages)
Foreach is the best choice when you want to use a Unified Call loop interface.
(MS has many classes such as datarowcollection .)
Note:
//////////////////////////////////////// //////////////////////////////////////// /////////////
Isinit:
If we do further experiments, we can find that the isinit here is the is keyword of C # (and the isinit is the core of the AS keyword)
Conversely, the is and as keywords are represented as isinit in the il code.
Isinst does not trigger an exception, but only checks whether the type is compatible.
//////////////////////////////////////// //////////////////////////////////////// /////////////
Castclass:
Castclass is used for forced conversion:
The castclass directive triggers the following:
Invalidcastexception-specified cast in not valid
If foreach is applied to an array of the string [] type (for example, the code above is changed to: foreach (INT s in array ))
This error can be detected during compilation. The prompt is: cannot convert type 'string' to 'int'
//////////////////////////////////////// //////////////////////////////////////// /////////////
About the iterator mode:
The iterator mode is a standard access method used to traverse collection classes. It can abstract the access logic from a collection class of the same type,
This avoids exposing the internal structure of the set to the client.
Features: [Reference]
"To ensure the smooth completion of the traversal process, you must ensure that the content of the set is not changed during the traversal process,
Therefore, the principle of ensuring the reliability of traversal is to use this set only in one thread, or to synchronize the traversal code in multiple threads. "
This also explains why C #. Net imposes strict restrictions on foreach, while for does not.
Iterator mode reference:
Http://www.emagister.cn/cursos-java%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%9A%E6%B7%B1%E5%85%A5%E6%8E%A2%E8% AE %A8iterator%E6%A8%A1%E5%BC%8F-simcour-1636693.htm