# the foregoing

In C#, the foreach statement can be used to iterate over elements in a number group, as follows:

    int[] arr1 = { 10 , 11 , 12 , 13};// Define an array
    foreach ( int item in arr )             // Enumerate elements
        Console.WtitLine( $"Item value : {item}" );
Copy the code

This code will produce the following output:

    Item value: 10 
    Item value: 11
    Item value: 12
    Item value: 13
Copy the code

You should be curious — why does a foreach statement automatically iterate over every element in a number array? To answer this question, we need to be clear: the foreach statement is just syntactic candy, or more generally, it’s just a shorthand that makes it easier for programmers.

So, the equivalent non-reductive writing of the above code is:

    static void Main()
    {
        int[] arr1 = { 10 , 11 , 12 , 13};// Create an array
        
        // Get the enumerator of the array object
        IEnumerator ie = arr1.GetEnumerator();
        
        // Move on to the next term
        while( ie.MoveNext() )  
        {
            int item = (int)ie.Current ;    //ie. Current defaults to Object and transforms to int;
            Console.WriteLine($"Item value: {item}");  / / output}}Copy the code

The output of this code is consistent with the code above using the foreach syntactic sugar:

    Item value: 10 
    Item value: 11
    Item value: 12
    Item value: 13
Copy the code

This code is the essence of foreach syntax sugar, that is, after being compiled by the C# compiler, the foreach statement will be compiled into this form of code, and this is the code that will be executed during the program.

As you can see from the above code, the essence of traversing a set of numbers using a foreach statement is: By calling the array object’s GetEnumerator() method, we get an object called an enumerator (an instance of a class that implements the IEnumerator interface). By manipulating this object, we can control, in turn, the array elements that the enumerator iterates over. The getEnumerator() method of an array type inherits and implements an IEnumerable interface. Any class that implements an IEnumerable interface is called an enumerable.

This discussion of foreach has led to two new concepts, enumerators and enumerable types, which are the focus of this article. We will explore how they work and how they work through a series of examples.


# The essence of an enumerator – an instance of a class that implements the IEnumerator interface

In the previous article, we mentioned that the enumerator is an instance of a class that implements the IEnumerator interface, so let’s look at what the IEnumerator interface looks like:

namespace System.Collections
{
    //
    / / in this paper:
    // Supports a simple iteration over a non-generic collection.
    public interface IEnumerator
    {
        //
        / / in this paper:
        // Gets the element in the collection at the current position of the enumerator.
        //
        // Return result:
        // The element in the collection at the current position of the enumerator.
        object? Current { get; }

        //
        / / in this paper:
        // Advances the enumerator to the next element of the collection.
        //
        // Return result:
        // true if the enumerator was successfully advanced to the next element; false if
        // the enumerator has passed the end of the collection.
        //
        / / exception:
        // T:System.InvalidOperationException:
        // The collection was modified after the enumerator was created.
        bool MoveNext();
        //
        / / in this paper:
        // Sets the enumerator to its initial position, which is before the first element
        // in the collection.
        //
        / / exception:
        // T:System.InvalidOperationException:
        // The collection was modified after the enumerator was created.
        void Reset(); }}Copy the code

As the code above shows, the IEnumerator interface contains three function members that need to be implemented by classes that inherit from it:

  • Current: This is a read-only property that returns a reference to type Object, so it can return any type of object, or in plain English — Current returns the Current position item in the sequence
  • MoveNext: Method of advancing the enumerator position to the next item in the collection. It returns a Boolean value indicating whether the new position is valid or has passed the end of the sequence, that is, the method returns true if the new position is valid, and false if it is invalid (such as the current position reaching the end of the sequence). Because the original position of the enumerator is before the first item in the sequence, MoveNext must be called before the first use of Current;
  • Reset: method of resetting a position to its original state;

Here is an enumerator class, ArrEnumerator, which can be thought of as a standard template for an enumerator class that inherits and implements the interface IEnumerator:

    using System ;
    using System.Collections ;
    class ArrEnumerator : IEnumerator
    {
        string[] Arrs ;       // This Arrs is the key, holding a copy of the sequence to be iterated over by the enumerator. For demonstration purposes, it is set to string[], which can also be int[], float[], or something else.
        int position = - 1 ;  // Sequence (array element) position, default is -1;
        
        // constructor
        public ArrEnumerator( string[] theArrs )  // theArrs is a reference to the sequence (array) object to be iterated over
        {
            // Save the copy through Arrs; Note: Arrs and theArrs are references to different sequence objects
            Arrs = new string[theArrs.Length] ;
            for( int i = 0; i < theArrs.Length ; i++ ) { Arrs[i] = theArrs[i] ; }}// Implement Current, return the Current sequence element pointed to by the enumerator, return a reference of type Object
        public object Current
        {
            get
            {
                if( position == - 1 )
                    throw new InvalidOperationException() ;
                if( position >= Arrs.Length )
                    throw new InvalidOperationException() ;
                    
                returnArrs[position] ; }}// Implement MoveNext() to move to the next position in the sequence
        public bool MoveNext() 
        {
            if( position < Arrs.Length - 1 )
            {
                position++ ;
                return true ;
            }
            else 
            {
                return false; }}// Implement reset to restore the enumerator to its initial state
        public void reset()
        {
            position = - 1; }}Copy the code

The code above describes a standard template for the enumerator class, although the implementation of the IEnumerator interface members may differ.

We can see from the above code that the enumerator is stateful, and this state is described by position. The state switch, the increment of position by +1, is done by the MoveNext() method. Each time after MoveNext(), as long as position< arrs.lenght, the new position is valid, MoveNext returns true. Otherwise, false is returned if the new position (state) moved to is invalid, position >= arrs.length; Continuing to call moveNext() when the state is position == arrs.length will always return false, while calling Current will raise an exception.


# The nature of enumerable types – Implements the IEnumerable interface

An enumerable class is a class that implements the IEnumerable interface. The IEnumerable interface has only one member — the GetEnumerator method, which returns the object’s enumerator.

The following code implements the IEnumerable interface:

namespace System.Collections
{
    //
    / / in this paper:
    // Exposes an enumerator, which supports a simple iteration over a non-generic collection.
    public interface IEnumerable
    {
        //
        / / in this paper:
        // Returns an enumerator that iterates through a collection.
        //
        // Return result:
        // An System.Collections.IEnumerator object that can be used to iterate through
        // the collection.
        IEnumerator GetEnumerator(); }}Copy the code

As mentioned earlier, the IEnumerable interface has only one member — the method GetEnumerator;

Next, we’ll demonstrate the standard declaration form, or standard template, for enumerable types with this code:

    using System.Collections ;
    class MyColors : IEnumerable
    {
        string[] colors = { "Red" , "Yellow" , "Blue"};public IEnumerator GetEnumerator() 
        {
            return newColorEnumerator(Colors) ; }}Copy the code