http://flier_lu.blogone.net/?id=1511638
The C # language draws a very practical Foreach statement from VB. For all instances of all classes that support the IENUMERABLE interface, the Foreach statement uses a unified interface to traverse its child, so that the cumbersome meter of the previously long FOR cycle is completely automatically completed by the compiler. A class that supports the IENUMERABLE interface is usually implemented with an internal IENUMERATOR interface and passing the IEnumerable.GeteNumerator function, allowing the user to complete traversal work such as a Foreach statement. This feature is very convenient, but it is necessary to pay a certain price. Juval Lowy Posted in MSDN Magazine 2004 No. 5 of Create Elegant Code with Anonymous Methods, Iterators, And Partial Classes, in detail, in detail, introduces itself support and other new features in C # 2.0. First, because the ienumerator.current attribute is a value of an Object type, the value type (VALUE TYPE) collection is in the foreach statement, each value must experience a useless Box and UNBOX operations; even if it is a reference type (Reference Type) The collection, when used by the foreach statement, there is also a redundant CastClass instruction to ensure the correctness of the enumerated value for type conversion.
The following is quoted:
Using system.collections;
Public Class tokens: ienumerable
{
...
Tokens f = new tokens (...);
FOREACH (String Item in F)
{
Console.writeLine (item);
}
...
}
The simple code above is automatically converted to
The following is quoted:
Tokens f = new tokens (...);
IENUMERATOR ENUM = f.Getenumerator ();
Try
{
Do {
String item = (string) enum.get_current (); // Redundancy Conversion
Console.writeLine (item);
WHILE (enum.movenext ());
}
Finally
{
If (Enum is idisposable) / / Need to verify that the class that implements the IENUMERATOR interface is supported by idisposable interface
{
(IDisposable) ENUM .dispose ();
}
}
Fortunately, the concept of generic is supported in C # 2.0, providing a strong type of generic version IENUMERABLE definition, the pseudo code is as follows:
The following is quoted:
Namespace system.collections.generic
{
Public Interface IEnumerable
{
IEnumerator
}
Public Interface IEnumerator
{
ItemType Current {Get;}
Bool movenext ();
}
}
In this way, the type of safety is safe when traversing the collection, but also can operate directly to the actual type of collections, avoid redundant conversion, and improve efficiency.
The following is quoted:
Using system.collections.Generic;
Public Class tokens: ienumerable
{
... // Realize IEnumerable
FOREACH (String Item in F)
{
Console.writeLine (item);
}
}
The above code is automatically converted to
The following is quoted:
Tokens f = new tokens (...);
IEnumerator
Try
{
Do {
String item = enum.get_current (); // No conversion
Console.writeLine (item);
WHILE (enum.movenext ());
}
Finally
{
If (enum) / / No need to verify that the class that implements the IENUMERATOR interface supports the IDisposable interface,
// Because all IENUMERATOR interfaces automatically generated by the compiler are supported
{
(IDisposable) ENUM .dispose ();
}
}
In addition to the redundant redundancy reducing performance over time, the other unhappy thing in the C # existing version is that IENUMERATOR interface is too much trouble. It is usually implemented by an in-line IENUMERATOR interface, and in addition to the GET_CURRENT () function, the functions of other parts are basically the same, such as
The following is quoted:
Public Class tokens: ienumerable
{
PUBLIC STRING [] Elements;
Tokens (String Source, Char [] DELIMITERS)
{
// Parse the string into tokens:
Elements = Source.Split (Delimiters);
}
Public ienumerator geteNumerator ()
{
Return New tokenenumerator (this);
}
// Inner Class Implements IENUMERATOR Interface:
Private class tokenenumerator: ienumerator
{
PRIVATE INT POSITION = -1;
Private tokens T;
Public Tokenenumenumerator (Tokens T)
{
THIS.T = T;
}
// Declare The Movenext Method Required by IEnumerator:
Public bool movenext ()
{
IF (position { POSITION ; Return True; } Else { Return False; } } // Declare the reset method required by ienumerator: Public void reset () { Position = -1; } // Declare The Current Property Required by ienumerator: Public Object Current { Get // get_current function { Return T.Elements [Position]; } } } ... } The Position and Tokens of the inline TOKENUMERATORALATOR are actually a class of each implementation IENUMERATOR interface, just the GET function of the Current property. This aspect C # 2.0 has made great improvements, adding the Yield keyword support, allowing code logical reuse. The length of the above code is only a few lines in C # 2.0, as follows: Using system.collections.Generic; Public Class tokens: ienumerable { Public IEnumerator { For (int i = 0; i Yield Elements [i]; } ... } The GetEnumerator function is a C # 2.0 support iteration block (Iterator Block), telling the compiler through Yield to return what value is returned, and then the compiler automatically completes the IENUMERATOR The following is quoted: Public IEnumerator { For (int i = 1; i <5; i ) { Yield Return I; IF (i> 2) Yield Break; // i> End of traversal } } In this way, it is easy to implement the IEnumerator interface, and it is convenient to support a variety of enumeration methods in a class, such as The following is quoted: Public Class CityCollection { String [] m_cities = {"New York", "Paris", "London"} Public IEnumerable { get { For (int i = m_cities.length-1; i> = 0; I -) Yield m_cities [i]; } } } Next, let's see where the compiler has done for us behind the language characteristics. Take the TOKENS class that supports the IENUMERABLE The following is quoted: Public Class tokens: ienumerable { Private Sealed Class GetENUMERATOR $ 00000000__ienumeratoriMPL : IEnumerator { Private int $ pc = 0; PRIVATE STRING $ _CURRENT; Private tokens Public INT I $ 00000001 = 0; // Implement IEnumerator String ienumerator { Return $ _CURRENT; } Bool IEnumerator { Switch ($ PC) { Case 0: { $ PC = -1; I $ 00000001 = 0; Break; } Case 1: { $ PC = -1; I $ 00000001 ; Break; } DEFAULT: { Return False; } } IF (i $ 00000001 < { $ _CURRENT = $ PC = 1; Return True; } Else { Return False; } } // Implement the Ienumerator interface Void IEnumerator.reset () { Throw new Exception (); } String ienumerator.get_current () { Return $ _CURRENT; } Bool ienumerator.movenext () { Return IEnumerator } / / Implement IDisposable interface void dispose () { } } Public IEnumerator { GetENUMERATOR $ 00000000__ienumerator 00000000__ienumerator 00000000__ienumerator 00000000__ienumerator 00000000__ienumerator 00000000__ienumeratoriMPL (); IMPL. Return IMPL; } } From the pseudo code we can see, the C # 2.0 compiler actually maintains a very similar internal class with the TokenEnumerator class that implements the IEnumerator interface in front to encapsulate the implementation of the IENUMERATOR The following is quoted: Using system; Using system.collections.Generic; Class Node { Public Node Public Node Public t item; } Public Class BinaryTree { Node Public void add (params t [] items) { Foreach (T Item in Items) Add (item); } Public Void Add (T Item) { // ... } Public IEnumerable { get { Return scaninorder (m_root); } } Ienumerable { IF (root.leftNode! = null) { Foreach (T Item in Scaninorder (Root.leftNode) { Yield item; } } Yield root.Item; Root.rightNode! = NULL) { Foreach (T Item in Scaninorder (Root.rightNode)) { Yield item; } } } } BinaryTree The following is quoted: Public Class BinaryTree { Private Sealed Class ScanIrder $ 00000000__ienumeratorImpl : Ienumerator { BinaryTree Node // ... } PRIVATE Sealed Class ScanIrder $ 000000__ienumerableImpl : Ienumerable { BinaryTree Node IEnumerator { Scaninorder $ 00000000__ienumeratoriMPL iPL. Impl.Root = this.root; Return IMPL; } IEnumerator ienumerable.getenumerator () { Scaninorder $ 00000000__ienumeratoriMPL iPL. Impl.Root = this.root; Return IMPL; } } Ienumerable { Scaninorder $ 00000000__ienumerableImpl IMPL. Impl.root = root; Return IMPL; } } Because the Scaninorder function content needs to use the root parameters, the package class of the IEnumerable The following is quoted: Public Class BinaryTree { Private Sealed Class GetENUMERATOR $ 00000000__ienumeratoriMPL: IEnumerator { Private int $ pc = 0; PRIVATE STRING $ _CURRENT; Private tokens Public INT I $ 00000001 = 0; Public IEnumerator Public IEnumerator Public T Item $ 00000001; Public T Item $ 00000002; Public Node // Implement IEnumerator String IEnumerator { Return $ _CURRENT; } Bool IEnumerator { Switch ($ PC) { Case 0: { $ PC = -1; IF (root.leftNode! = null) { __wrap $ 00000003 = Goto scanleft; } Else { Goto GetItem; } } Case 1: { Return False; } Case 2: { Goto scanleft; } Case 3: { $ PC = -1; Root.rightNode! = NULL) { __Wrap $ 00000004 = Goto scanright; } Else { Return False; } Break; } Case 4: { Return False; } Case 5: { Goto scanright; } DEFAULT: { Return False; } Scanleft: $ PC = 1; IF (__ wrap $ 00000003.MOVENEXT ()) { $ _CURRENT = Item $ 00000001 = __Wrap $ 00000003.Get_current (); $ PC = 2; Return True; } GetItem: $ PC = -1; IF (__ wrap $ 00000003! = NULL) { (Idisposable) __ wrap $ 00000003) .dispose (); } $ _CURRENT = root.item; $ PC = 3; Return True; Scanright: $ PC = 4; IF (__ wrap $ 00000004.MOVENEXT ()) { $ _CURRENT = $ 00000002 = __wrap $ 00000004.get_current (); $ PC = 5; Return True; } Else { $ PC = -1; IF (__ wrap $ 00000004! = NULL) { (Idisposable) __ wrap $ 00000004) .dispose (); } Return False; } } / / Implement IDisposable interface void dispose () { Switch ($ PC) { Case 1: Case 2: { $ PC = -1; IF (__ wrap $ 00000003! = NULL) { (Idisposable) __ wrap $ 00000003) .dispose (); } Break; } Case 4: Case 5: { $ PC = -1; IF (__ wrap $ 00000004! = NULL) { (Idisposable) __ wrap $ 00000004) .dispose (); } Break; } } } } } Through the top of the pseudo code, we can see that C # 2.0 is actually a recursive segment that is completed by a limited state machine for the argument of $ PC, which may be because the limited state machine can easily generate it through the program. . The Dispose () function is responsible for processing the intermediate variable of the state machine. Interested in further understanding of the iterative features, you can read the ITERATORS related articles on the BLOG of Grant Ri. After understanding the implementation principle of Iterators, then those discussions will not be confused by their appearances: D