Clause 11: Use foreach statement first

Source: Internet
Author: User
Tags finally block
Clause 11: Priority ForeachLoop statement

 

The foreach Statement of C # is not only a variation of do... while or for loop statements. It will generate the best traversal code for our set. In fact, the definition of the foreach statement is closely related to the set interface in the. NET Framework. For some Special Collection types, the C # compiler will produce code with the best efficiency. When traversing a set, we should use the foreach statement instead of other loop structures. For example, for the following three cycles:

Clause 11: preference for foreach loop statement 70

Int [] Foo = new int [100];

// Loop 1:

Foreach (int I in Foo)

Console. writeline (I. tostring ());

// Cycle 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 later), the code generated by 1st loops is optimal and requires the least characters to be entered, therefore, programmers are more efficient in development. (However, in the C #1.0 compiler, the code generated by 1st loops is slow, and the code generated by 2nd loops is the best .) Most C and C ++ Programmers think that the most efficient 3rd loop is the worst choice. By putting the length variable out of the loop, we actually prevent the JIT compiler from removing the range check in the loop.

C # code runs in a secure and hosted environment. Each memory location is checked, including an array index. In fact, the code generated by the 3rd loops is equivalent to the following code:

// Loop 3, equivalent to the code generated by the 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 ();

}

JIT and C # compilers do not "like". We use this method to help them. Putting the Length attribute outside the loop will only allow the JIT compiler to do more work, and the generated code will be slower. CLR will ensure that the code we write will not abuse the memory of the variable. CLR will generate an array boundary (not the above Len variable) test before accessing each specific array element. If we write code as above, each array boundary test will be executed twice.

In each iteration of the loop, we need to check the array index twice. The reason why 1st loops and 2nd loops are faster is that the C # compiler and JIT compiler can ensure that the array boundary in the loop is safe. As long as the loop variable is not the Length attribute of the array, the array boundary check is executed during each iteration.

For the C # compiler of version 1.0, the reason for the slow code produced by using the foreach Statement on the array is the packing operation (for more information about packing, see section 17 ). In. net, arrays are type-safe. The C # compiler after version 1.1 generates different il values for arrays and other sets. In the code generated by the 1.0 compiler, using the foreach Statement on the array actually traverses the array through the ienumerator interface, which leads to the packing and unpacking operations:

Ienumerator it = Foo. getenumerator ();

While (it. movenext ())

{

Int I = (INT) It. Current; // boxing and unpacking will appear here.

Console. writeline (I. tostring ());

}

On the contrary, for the C # compiler after Version 1.1, using the foreach Statement on the array will generate a structure similar to the following:

For (INT Index = 0;

Index <Foo. length;

Index ++)

Console. writeline (FOO [Index]. tostring ());

Since foreach statements always generate the best code, we don't have to remember which structure will generate the most efficient loop structure-foreach and the compiler will do this for us.

If efficiency cannot convince everyone, let's take a look at language interoperability. There are always some people (most of them have experience using other programming languages) who firmly believe that the starting index variable of the array should start from 1 (rather than 0. No matter how hard we persuade them, they cannot change their habits .. The net development team has made every effort on this issue. We can use the following Initialization Method in the C # language to obtain an array with the starting index not 0:

Clause 11: preference of foreach loop statement 73

// Create a one-dimensional array in the range of [1. 5].

Array test = array. createinstance (typeof (INT ),

New int [] {5}, new int [] {1 });

Many people may backend such code and use an array whose initial index is 0. But there are always some stubborn people. No matter how hard you work, these people will always index the array from 1. Fortunately, in this case, we can use the foreach statement to bypass the mixed Compiler:

Foreach (Int J in test)

Console. writeline (j );

The foreach statement here knows how to obtain the upper and lower bounds of the array, so you don't have to bother us-and its efficiency is as fast as the for loop we write, no matter what the lower bounds of the array are, we can use this method to work normally.

In addition, the foreach statement can bring us other benefits. The cyclic variables are read-only -- that is, we cannot replace the set object in the foreach statement. There is also an explicit forced transformation. If the object type stored in the collection is incorrect, an exception is thrown in the iteration statement.

For multi-dimensional arrays, The foreach statement has similar benefits. Suppose we want to create a chessboard, we may write the following two pieces of code:

Private square [,] _ theboard = new square [8, 8];

// Other code:

For (INT I = 0; I <_ theboard. getlength (0); I ++)

For (Int J = 0; j <_ theboard. getlength (1); j ++)

_ Theboard [I, j]. paintsquare ();

Using the foreach statement, we can simplify the above traversal Code as follows:

Foreach (square SQ in _ theboard)

Sq. paintsquare ();

The foreach statement generates the correct traversal Code regardless of the number of dimensions of the array. If we create another 3D Board later, the foreach loop above will still work normally. Other handwritten cyclic code needs to be changed:

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 works normally for multidimensional arrays with different lower bounds on each dimension. I will not write such sample code here. If someone uses such a set, we need to know that the foreach statement can also process it.

If we are using arrays at the beginning and then need to switch to other data structures, the foreach statement allows us not to change the vast majority of code to maintain code flexibility. Suppose we are starting with a simple array:

Int [] Foo = new int [100];

However, after a while, we found that the array could not easily process some of the features we needed. At this time, we choose to change the array to arraylist:

// Set the initial size:

Arraylist Foo = new arraylist (100 );

After this change, any handwritten for loop code will be damaged:

Int sum = 0;

For (INT Index = 0;

// The following Code cannot be compiled: arraylist uses count instead of length.

Index <Foo. length;

Index ++)

// The following Code cannot be compiled: Foo [Index] is an object instead of an int.

Sum + = Foo [Index];

Using the foreach statement, it will compile different codes and automatically convert each operand to the correct type. We do not need to make any changes in the Code. In fact, using the foreach statement can not only be changed to the standard set type-foreach can be used for any set type.

If we support the rules defined by the. NET environment for the set, you can use foreach to traverse our type members. To make the foreach statement regard a class as a set type, the class must have some attributes. There are three ways to make a Class A Collection class: The type has a public getenumerator () method, the type explicitly implements the ienumerable interface, and the type implements the ienumerator interface [25].

Clause 11: Use foreach statement 74 first

Finally, the foreach statement will bring additional benefits to resource management. The ienumerable interface only contains one method: getenumerator (). Using the foreach statement on a type that supports the ienumerable interface produces code similar to 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;

}

}

If the compiler can determine the implementation of the type pair idisposable interface, it will automatically optimize the statements in the Finally block [26].

To sum up, foreach is a very useful statement. It uses the most efficient structure to generate the correct code, including "Up and down bounds index of array", "multi-dimensional array traversal", and "operand transformation, in addition, the most efficient cycle structure is generated. It is the best way to traverse a set. Using it, the code we write will be "durable" and simple at the beginning. Using foreach may bring us little improvement in development efficiency, but with the passage of time, its benefits will continue to grow.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.