Objective C # Principle 11: select a foreach Loop
Item 11: prefer foreach Loops
The foreach Statement of C # changes from do, while, or for loop statements. It is relatively better. It can generate the best Iteration for any set.Code. Its definition is based on the set interface in the. NET Framework, and the compiler will generate the best code for the actual set. When you iterate on a set, you can use foreach to replace other loop structures. Check the following three cycles:
Int [] Foo = new int [100];
// Loop 1:
Foreach (int I in Foo)
Console. writeline (I. tostring ());
// Loop 2:
For (INT Index = 0; index <Foo. length; index ++)
Console. writeline (FOO [Index]. tostring ());
// Loop 3:
Int Len = Foo. length;
For (INT Index = 0; index <Len; index ++)
Console. writeline (FOO [Index]. tostring ());
Loop 1 is the best for the current C # Compiler (Version 1.1 or later. At least it requires less input, which will improve your personal development efficiency. (The C # compiler of 1.0 is much slower than loop 1, so loop 2 is the best version .) Loop 3, most C or C ++ProgramMembers will think it is the most effective, but it is the worst. Because the length value of the variable is retrieved outside the loop, the JIT compiler is prevented from removing the Boundary Detection from the loop.
C # The code runs in secure managed code. Each memory block in the environment, including data indexes, is monitored. Expand it a bit. The code for loop 3 is actually like this:
// Loop 3, as generated by Compiler:
Int Len = Foo. length;
For (INT Index = 0; index <Len; index ++)
{
If (index <Foo. length)
Console. writeline (FOO [Index]. tostring ());
Else
Throw new indexoutofrangeexception ();
}
C #'s JIT compiler is different from you. It tries to help you do this. You wanted to put the Length attribute out of the loop, but made compilation do more, thus reducing the speed. One of the content that CLR should ensure is that you cannot write code that allows variables to access non-memory. When accessing each actual set, the runtime ensures that the boundary (not the Len variable) of each set is checked. You divide a boundary check into two parts.
You still need to perform index detection for each iteration of the loop, and it is twice. Loop 1 and loop 2 are faster because the JIT compiler of C # can verify the boundary of the array to ensure security. If any cyclic variable is not the length of the data, the boundary detection will occur in each iteration. The JIT compiler refers to the compiler that compiles the Il code at a cost, instead of the compiler that compiles C # code or other code into il code. In fact, we can use insecure options to force JIT not to perform such detection, so as to improve the running speed .)
The original C # compiler generates very slow code for foreach and arrays because it involves packing. Packing will be discussed in principle 17. Arrays are safe. Now foreach can generate il code different from other sets for arrays. For this version of the array, it no longer uses the ienumerator interface, that is, this interface requires packing and unpacking.
Ienumerator it = Foo. getenumerator ();
While (it. movenext ())
{
Int I = (INT) It. Current; // box and Unbox here.
Console. writeline (I. tostring ());
}
Instead, the foreach statement generates the following structure for the array:
For (INT Index = 0; index <Foo. length; index ++)
Console. writeline (FOO [Index]. tostring ());
Note the differences between arrays and collections. An array is a one-time allocation of continuous memory. A set can be dynamically added and modified. It is generally implemented using a linked list. The support for the Sawtooth array in C # is a compromise .)
Foreach always guarantees the best code. You don't have to worry about which structure of the loop has a higher efficiency: foreach and the compiler work for you.
If you are not satisfied with efficiency, for example, you must have language interaction. Some people in this world (yes, they are using otherProgramming Language) Unswervingly think that the index of the array starts from 1, rather than 0. No matter how hard we work, we cannot break this habit .. Net Development Team has tried. Therefore, you have to write initialization code in C #, that is, the array starts from a non-zero value.
// Create a single dimension array.
// Its range is [1 .. 5]
Array test = array. createinstance (typeof (INT ),
New int [] {5}, new int [] {1 });
This code should be enough to scare everyone ). But some people are stubborn and will count from 1 without recognizing how you work. Fortunately, this is one of those problems, and you can let the compiler "cheat ". Use foreach to iterate the test array:
Foreach (Int J in test)
Console. writeline (j );
The foreach statement knows how to check the upper and lower limits of an array, so you should do so, and this is the same as the speed of the for loop, so you don't have to worry that someone uses that as the lower limit.
For multi-dimensional arrays, foreach gives you the same benefits. Suppose you are creating a chessboard. You will write two sections of Code as follows:
Private square [,] _ theboard = new square [8, 8];
// Elsewhere in code:
For (INT I = 0; I <_ theboard. getlength (0); I ++)
For (Int J = 0; j <_ theboard. getlength (1); j ++)
_ Theboard [I, j]. paintsquare ();
Instead, you can simply draw the board as follows:
Foreach (square SQ in _ theboard)
Sq. paintsquare ();
I do not agree with this method. It hides the logical relationship between rows and columns of arrays. Loops take precedence over rows. If you don't want them in this order, this loop is not good .)
The foreach statement generates appropriate code to iterate all the Dimension Data in the array. If you want to create a 3D board in the future, the foreach loop will still work the same, while the other loop will do the following modification:
For (INT I = 0; I <_ theboard. getlength (0); I ++)
For (Int J = 0; j <_ theboard. getlength (1); j ++)
For (int K = 0; k <_ theboard. getlength (2); k ++)
_ Theboard [I, j, k]. paintsquare ();
Although there seems to be a lot of code, I think that as long as it is a programmer, it can be seen at a glance that this is a three-dimensional array loop, but for foreach, no one can tell at a glance what it is doing! Personal Understanding. Of course, it depends on how you know each other. This is certainly an advantage of foreach .)
In fact, the foreach loop can also work on a multi-dimensional array with different lower limits of each dimension ). I don't want to write such code, even for example. But when someone writes such a set at a certain time, foreach is competent.
Foreach also gives you great scalability. When you find that you need to modify the underlying data structure in the array, it can ensure that the Code is not modified as much as possible. We will discuss this from a simple array:
Int [] Foo = new int [100];
Suppose later, you find that it does not have some functions of the array class, and you just need these functions. You may simply change an array to an arraylist:
// Set the initial size:
Arraylist Foo = new arraylist (100 );
Any code that uses the for loop is corrupted:
Int sum = 0;
For (INT Index = 0;
// Won't compile: arraylist uses count, not length
Index <Foo. length;
Index ++)
// Won't compile: Foo [Index] is object, not int.
Sum + = Foo [Index];
However, the foreach loop can be automatically compiled into different code to convert the appropriate type based on the object to be operated. You do not need to change anything. It is not only applicable to standard arrays, but also to any other set types.
If your set supports rules in the. NET environment, your users can use foreach to iterate your data type. To make the foreach statement think of it as a collection type, a class should have one of the most attributes: the implementation of the public method getenumerator () can constitute a collection class. A clear implementation of the ienumerable interface can generate a collection class. You can also implement a collection class by implementing the ienumerator interface. Foreach can work on any one.
One benefit of foreach is resource management. The ienumerable Interface contains a method: getenumerator (). The foreach statement generates the following code on the enumerated type and is optimized:
Ienumerator it = Foo. getenumerator () as ienumerator;
Using (idisposable disp = it as idisposable)
{
While (it. movenext ())
{
Int ELEM = (INT) It. Current;
Sum + = ELEM;
}
}
If the enumerator implements the idisposable interface, the compiler can automatically optimize the code to a Finally block. But it is important for you to understand this. In any case, foreach generates the correct code.
Foreach is a widely used statement. It is the correct code for the upper and lower limits of arrays, iterates multi-dimensional arrays, and forcibly converts them to appropriate types (using the most effective structure). Also, this is the most important, generate the most effective loop structure. This is the most effective method for iterative sets. In this way, the code you write is more persistent (meaning the code won't be changed too much due to errors), and the first time you write the code is more concise. This is a small improvement in productivity and will accumulate over time.
======================================
Item 11: prefer foreach Loops
The C # foreach statement is more than just a variation of the do, while, or for loops. it generates the best iteration Code for any collection you have. its definition is tied to the Collection interfaces in. net Framework, and the C # compiler generates the best code for the particle type of collection. when you iterate collections, use foreach instead of other looping constructs. examine these three loops:
Int [] Foo = new int [100];
// Loop 1:
Foreach (int I in Foo)
Console. writeline (I. tostring ());
// Loop 2:
For (INT Index = 0;
Index <Foo. length;
Index ++)
Console. writeline (FOO [Index]. tostring ());
// Loop 3:
Int Len = Foo. length;
For (INT Index = 0;
Index <Len;
Index ++)
Console. writeline (FOO [Index]. tostring ());
For the current and future C # compilers (Version 1.1 and up), loop 1 is best. it's even less typing, so your personal producti?goes up. (the C #1.0 compiler produced much slower code for loop 1, so loop 2 is best in that version .) loop 3, the construct most C and C ++ programmers wocould view as most efficient, is the worst option. by hoisting the length variable out of the loop, you make a change that hinders the JIT compiler's chance to remove range checking inside the loop.
C # code runs in a safe, managed environment. Every memory location is checked, including array indexes. Taking a few liberties, the actual code for loop 3 is something like this:
// Loop 3, as generated by Compiler:
Int Len = Foo. length;
For (INT Index = 0;
Index <Len;
Index ++)
{
If (index <Foo. length)
Console. writeline (FOO [Index]. tostring ());
Else
Throw new indexoutofrangeexception ();
}
The jit c # compiler just doesn't like you trying to help it this way. your attempt to hoist the length property access out of the loop just made the JIT compiler do more work to generate even slower code. one of the CLR guarantees is that you cannot write code that overruns the memory that your variables own. the runtime generates a test of the actual Array Bounds (not your Len variable) before accessing each particle array element. you get one bounds check for the price of two.
You still pay to check the array index on every iteration of the loop, and you do so twice. the reason loops 1 and 2 are faster is that the C # compiler and the JIT compiler can verify that the bounds of the loop are guaranteed to be safe. anytime the loop variable is not the length of the array, the bounds check is performed med on each iteration.
The reason that foreach and arrays generated very slow code in the original C # compiler concerns boxing, which is covered extensively in item 17. arrays are type safe. foreach now generates different il for arrays than other collections. the array version does not use the ienumerator interface, which wowould require boxing and unboxing:
Ienumerator it = Foo. getenumerator ();
While (it. movenext ())
{
Int I = (INT) It. Current; // box and Unbox here.
Console. writeline (I. tostring ());
}
Instead, the foreach statement generates this construct for Arrays:
For (INT Index = 0;
Index <Foo. length;
Index ++)
Console. writeline (FOO [Index]. tostring ());
Foreach always generates the best code. You don't need to remember which construct generates the most efficient looping construct: foreach and the compiler will do it for you.
If efficiency isn' t enough for you, consider language InterOP. some folks in the world (Yes, most of them use other programming versions) stronugly believe that index variables start at 1, not 0. no matter how much we try, we won't break them of this habit. the. net team tried. you have to write this kind of initialization in C # To get an array that starts at something other than 0:
// Create a single dimension array.
// Its range is [1 .. 5]
Array test = array. createinstance (typeof (INT ),
New int [] {5}, new int [] {1 });
This code shoshould be enough to make anybody cringe and just write arrays that start at 0. but some people are stubborn. try as you might, they will start counting at 1. luckily, this is one of those problems that you can foist off on the compiler. iterate the test Array Using foreach:
Foreach (Int J in test)
Console. writeline (j );
The foreach statement knows how to check the upper and lower bounds on the array, so you don't have toand it's just as fast as a hand-coded for loop, no matter what different lower bound someone decides to use.
Foreach adds other language benefits for you. the loop variable is read-only: You can't replace the objects in a collection using foreach. also, there is explicit casting to the correct type. if the collection contains the wrong type of objects, the iteration throws an exception.
Foreach gives you similar benefits for multidimen1_arrays. Suppose that you are creating a chess board. You wocould write these two fragments:
Private square [,] _ theboard = new square [8, 8];
// Elsewhere in code:
For (INT I = 0; I <_ theboard. getlength (0); I ++)
For (Int J = 0; j <_ theboard. getlength (1); j ++)
_ Theboard [I, j]. paintsquare ();
Instead, you can simplify painting the board this way:
Foreach (square SQ in _ theboard)
Sq. paintsquare ();
The foreach statement generates the proper code to iterate each SS all dimensions in the array. If you make a 3D chessboard in the future, the foreach loop just works. The other loop needs modification:
For (INT I = 0; I <_ theboard. getlength (0); I ++)
For (Int J = 0; j <_ theboard. getlength (1); j ++)
For (int K = 0; k <_ theboard. getlength (2); k ++)
_ Theboard [I, j, k]. paintsquare ();
In fact, the foreach loop wocould work on a multidimensional array that had different lower bounds in each direction. I don't want to write that kind of code, even as an example. but when someone else codes that kind of collection, foreach can handle it.
Foreach also gives you the flexibility to keep much of the code intact if you find later that you need to change the underlying data structure from an array. We started this discussion with a simple array:
Int [] Foo = new int [100];
Suppose that, at some later point, you realize that you need capabilities that are not easily handled by the array class. You can simply change the array to an arraylist:
// Set the initial size:
Arraylist Foo = new arraylist (100 );
Any hand-coded for loops are broken:
Int sum = 0;
For (INT Index = 0;
// Won't compile: arraylist uses count, not length
Index <Foo. length;
Index ++)
// Won't compile: Foo [Index] is object, not int.
Sum + = Foo [Index];
However, the foreach loop compiles to different code that automatically casts each operand to the proper type. no changes are needed. it's not just changing to standard collections classes, eitherany collection type can be used with foreach.
Users of your types can use foreach to iterate your SS members if you support. net Environment's rules for a collection. for the foreach statement to consider it a collection type, a class must have one of a number of properties. the presence of a public getenumerator () method makes a collection class. explicitly implementing the ienumerable interface creates a collection type. implementing the ienumerator interface creates a collection type. foreach works with any of them.
Foreach has one added benefit regarding resource management. The ienumerable Interface contains one method: getenumerator (). The foreach statement on an enumerable type generates the following, with some optimizations:
Ienumerator it = Foo. getenumerator () as ienumerator;
Using (idisposable disp = it as idisposable)
{
While (it. movenext ())
{
Int ELEM = (INT) It. Current;
Sum + = ELEM;
}
}
The compiler automatically optimizes the code in the finally clause if it can determine for certain whether the enumerator implements idisposable. but for you, it's more important to see that, no matter what, foreach generates correct code.
foreach is a very versatile statement. it generates the right code for upper and lower bounds in arrays, iterates multidimensional arrays, coerces the operands into the proper type (using the most efficient construct), and, on top of that, generates the most efficient looping constructs. it's the best way to iterate collections. with it, you'll create code that is more likely to last, and it's simpler for you to write in the first place. it's a small productivity improvement, but it adds up over time.