View the C # (3) -- foreach statement through IL

Source: Internet
Author: User
Tags mscorlib
View C # (3) through IL)
Foreach statement

Address: http://www.cnblogs.com/AndersLiu/archive/2009/02/04/csharp-via-il-foreach.html

Original: Anders Liu

Abstract: A foreach statement is an important loop statement in C #. It is used to traverse every element in an array or object set. This article describes the IL code generated by the compiler for the foreach statement when dealing with arrays, IEnumerable interfaces, and custom types.

A foreach statement is an important loop statement in C #. It is used to traverse every element in an array or object set. The basic format of the foreach statement is as follows:

Code 1-Basic foreach Statement Form

<Br/> foreach ([variable] in [set]) <br/> [statement or statement block] <br/>

The foreach statement is used to extract an element from the [set] in code 1 and put it in the [variable] for each loop, and then execute a [statement or statement block]. Note: In [statements or statement blocks], [variables] are read-only. That is to say, you can only access the value of [variable], but cannot assign values to it.

Use arrays in foreach statements

Arrays are the simplest Collection types and are most commonly used in foreach statements. Code 2 provides a simple foreach loop.

Code 2-Use the array foreach Loop

<Br/> static void Test (int [] values) <br/>{< br/> foreach (int I in values) <br/> Console. writeLine (I); <br/>}< br/>

Code 3 provides the IL generated by the compiler using the ILDASM tool for the above Code. I used C # code to provide comments for ease of viewing.

Code 3-IL corresponding to Code 2

<Br/>. method private hidebysig static void Test (int32 [] values) di-managed <br/>{< br/> // code size 34 (0x22) <br/>. maxstack 2 <br/>. locals init (int32 V_0, // I <br/> int32 [] V_1, // copy of values <br/> int32 V_2, // intermediate variable, use an array subscript (Cyclic variable) <br/> bool V_3) // an intermediate variable to determine the cyclic termination condition <br/> IL_0000: nop <br/> IL_0001: nop </p> <p> // V_1 = values <br/> IL_0002: ldarg.0 <br/> IL_0003: stloc.1 </p> <p> // V_2 = 0 <br/> IL_0004: ldc. i4.0 <br/> IL_0005: stloc.2 </p> <p> // goto IL_0017 <br/> IL_0006: br. s IL_0017 </p> <p> // V_0 = V_1 [V_2] <br/> IL_0008: ldloc.1 <br/> IL_0009: ldloc.2 <br/> IL_000a: ldelem. i4 <br/> IL_000b: stloc.0 </p> <p> // Console. writeLine (V_0) <br/> IL_000c: ldloc.0 <br/> IL_000d: call void [mscorlib] System. console: WriteLine (int32) <br/> IL_0012: nop </p> <p> // V_2 ++ <br/> IL_0013: ldloc.2 <br/> IL_0014: ldc. i4.1 <br/> IL_0015: add <br/> IL_0016: stloc.2 </p> <p> // V_3 = V_2 <V_1.Length <br/> IL_0017: ldloc.2 <br/> IL_0018: ldloc.1 <br/> IL_0019: ldlen <br/> IL_001a: conv. i4 <br/> IL_001b: clt <br/> IL_001d: stloc.3 </p> <p> // if (V_3) goto IL_0008 <br/> IL_001e: ldloc.3 <br/> IL_001f: brtrue. s IL_0008 </p> <p> IL_0021: ret <br/>}// end of method Program: Test <br/>

The intermediate variable V_2 in code 3 (no corresponding variable exists in Code 2) the "V_2 ++" at IL_0013 exposes the structural features of this Code-the same as the for statement.

Therefore, we can get the first conclusion-for foreach statements using arrays, the compiler translates them into IL code similar to for statements. However, there is a difference between the two. As mentioned above, the array elements in the foreach statement are read-only.

Use a general set (ICollection) in a foreach statement)

Next, we try to use a general set in the foreach loop. Here we use a parameter that implements the ICollection <int> interface. See Code 4.

Code 4-use the ICollection foreach Loop

<Br/> static void Test (ICollection <int> values) <br/> {<br/> foreach (int I in values) <br/> Console. writeLine (I); <br/>}< br/>

Code 5 is the IL code corresponding to code 4. It also provides comments for C # statements.

Code 5-code 4 corresponds to the IL

<Br/>. method private hidebysig static void Test (class [mscorlib] System. collections. generic. ICollection '1 <int32> values) ASME managed <br/>{< br/> // code size 55 (0x37) <br/>. maxstack 2 <br/>. locals init (int32 V_0, // I <br/> class [mscorlib] System. collections. generic. IEnumerator '1 <int32> V_1, <br/> bool V_2) <br/> IL_0000: nop <br/> IL_0001: nop </p> <p> // V_1 = (IEnumerator <int>) values. getEnumerator () <Br/> IL_0002: ldarg.0 <br/> IL_0003: callvirt instance class [mscorlib] System. Collections. Generic. IEnumerator '1 <! 0> class [mscorlib] System. collections. generic. IEnumerable '1 <int32 >:: GetEnumerator () <br/> IL_0008: stloc.1 </p> <p>. try <br/> {<br/> // goto IL_0019 <br/> IL_0009: br. s IL_0019 </p> <p> // V_0 = V_1.Current <br/> IL_000b: ldloc.1 <br/> IL_000c: callvirt instance! 0 class [mscorlib] System. collections. generic. IEnumerator '1 <int32 >:: get_Current () <br/> IL_0011: stloc.0 </p> <p> // Console. writeLine (V_0) <br/> IL_0012: ldloc.0 <br/> IL_0013: call void [mscorlib] System. console: WriteLine (int32) <br/> IL_0018: nop </p> <p> // V_2 = V_1.MoveNext () <br/> IL_0019: ldloc.1 <br/> IL_001a: callvirt instance bool [mscorlib] System. collections. IEnumerator: MoveNext () <br/> IL _ 001f: stloc.2 </p> <p> // if (V_2) goto IL_000b else goto IL_0035 // (IL_0035 = ret) <br/> IL_0020: ldloc.2 <br/> IL_0021: brtrue. s IL_000b <br/> IL_0023: leave. s IL_0035 <br/>} // end. try <br/> finally <br/> {<br/> // if (V_1! = Null) V_1.Dispose () <br/> IL_0025: ldloc.1 <br/> IL_0026: ldnull <br/> IL_0027: ceq <br/> IL_0029: stloc.2 <br/> IL_002a: ldloc.2 <br/> IL_002b: brtrue. s IL_0034 <br/> IL_002d: ldloc.1 <br/> IL_002e: callvirt instance void [mscorlib] System. IDisposable: Dispose () <br/> IL_0033: nop <br/> IL_0034: endfinally <br/>}// end handler <br/> IL_0035: nop <br/> IL_0036: ret <br/>}// end of method Program: Test <br/>

We can see that at the beginning of the method, the ICollection type GetEnumerable () method is called to obtain an enumerator (IEnumerator ).

The part from IL_0009 to IL_0023 (. try block) is actually a while loop structure. However, when using C # To write a program, the condition is determined at the top of the while statement, and the corresponding IL is determined at the bottom of the entire loop.

Therefore, we get the second conclusion: for a general set, the foreach statement will first convert the set to the IEnumerable interface (the ICollection interface inherits from IEnumerable), and then obtain the IEnumerator object, and then traverse through the while loop.

At the same time, it also corrected the previous statement, that is, the foreach statement is not only applicable to "sets", as long as it is an object that can be enumerated (implements the IEnumerable Interface ), you can use foreach for traversal.

Use custom types in foreach statements

Let's go further. If we write a type ourselves, can we use foreach to traverse its object as a "set?

Through the above conclusion 2, we can infer that as long as the type we write implements the IEnumerable interface (including the IEnumerable generic interface), we should be able.

The answer is yes.

However, the fact is that even if the IEnumerable interface is not implemented, as long as the signature is provided as the public IEnumerator GetEnumerator () method, a custom type object can still be traversed through foreach. For example, the Type given in Code 6.

Code 6-custom types that can be used in foreach statements

<Br/> public class MyEnumerator <br/> {<br/> public IEnumerator <int> GetEnumerator () <br/>{< br/> for (var I = 0; I <10; I ++) <br/> yield return I; <br/>}< br/>

Note that the GetEnumerator method must be public without parameters, and the return value must be IEnumerator or IEnumerator <T>. This restriction is actually the same as implementing the IEnumerable interface. Therefore, you only need to understand this feature. If you really need to use the foreach statement to traverse your own type, you still need to implement the IEnumerable interface.

The following is an example of using a custom foreach statement. The IL generated by the compiler is similar to code 5, which is not listed here. You can use ILDasm to take a look.

Code 7-use a custom foreach statement

<Br/> static void Test (MyEnumerator values) <br/> {<br/> foreach (int I in values) <br/> Console. writeLine (I); <br/>}< br/>Summary

This section describes how to use arrays, General sets, and custom types in foreach statements.

When using arrays, foreach and for are equivalent (but foreach can only perform read-only traversal), so we don't have to worry about the overhead when getting and using enumerators.

When using a general set and a custom type, you actually retrieve the enumerator by calling the GetEnumerator method and traverse it through the while loop. Although a custom type can be used in combination with the foreach statement as long as the GetEnumerator method with a specific signature is implemented, we recommend that you implement the IEnumerable interface when developing such a type.

Returned Directory: view C # Through IL #

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.