SoFunction
Updated on 2025-03-07

Detailed explanation of the principle of foreach implementation in C#

This article mainly records my experience in learning the principle of foreach traversal in C#.

Traversing elements in a collection is an operation that is often involved in all encodings, so most programming languages ​​write this process into syntax, such as foreach in C#. You will often see the following traversal code:

var lstStr = new List<string> { "a", "b" };
   foreach (var str in lstStr)
      {
        (str);
      }

The actual execution process of this code:

var lstStr = new List<string> {"a", "b"};
   IEnumerator<string> enumeratorLst = ();
   while (())
      {
        ();
      }

You will find that there are GetEnumerator() methods and IEnumerator<string> types, which involves the concepts of enumerable types and enumerators.

For easy understanding, the following are non-generic examples:

// Summary:// Exposes an enumerator that supports simple iteration on non-generic collections.  public interface IEnumerable
  {
    // Summary:    // Returns an enumerator that loops through the collection.    //
    // Return result:    // Objects that can be used to iterate over the collection.    IEnumerator GetEnumerator();
  }

The class that implements this interface is called an enumerable type, and is a flag that can be traversed with foreach.

The return value of the method GetEnumerator() is an enumerator, which can be understood as a cursor.

// Summary:// Supports simple iteration of non-generic collections.  public interface IEnumerator
  {
    // Summary:    // Get the current element in the collection.    //
    // Return result:    // The current element in the collection.    //
    // Exception:    //  :
    // Enumerations are positioned before or after the first element of the collection.    object Current { get; }

    // Summary:    // Propel the enumeration to the next element of the collection.    //
    // Return result:    // true if the enumeration is successfully advanced to the next element; false if the enumeration passes the end of the set.    //
    // Exception:    //  :
    // The collection is modified after the enumeration is created.    bool MoveNext();
    //
    // Summary:    // Set the enumeration number to its initial position, which is before the first element in the collection.    //
    // Exception:    //  :
    // The collection is modified after the enumeration is created.    void Reset();
  }

Here is an example of customizing an iterator (/en-us/library/):

using System;
using ;

// Simple business object.
public class Person
{
  public Person(string fName, string lName)
  {
     = fName;
     = lName;
  }

  public string firstName;
  public string lastName;
}

// Collection of Person objects. This class
// implements IEnumerable so that it can be used
// with ForEach syntax.
public class People : IEnumerable
{
  private Person[] _people;
  public People(Person[] pArray)
  {
    _people = new Person[];

    for (int i = 0; i < ; i++)
    {
      _people[i] = pArray[i];
    }
  }

// Implementation for the GetEnumerator method.
  IEnumerator ()
  {
    return (IEnumerator) GetEnumerator();
  }

  public PeopleEnum GetEnumerator()
  {
    return new PeopleEnum(_people);
  }
}

// When you implement IEnumerable, you must also implement IEnumerator.
public class PeopleEnum : IEnumerator
{
  public Person[] _people;

  // Enumerators are positioned before the first element
  // until the first MoveNext() call.
  int position = -1;

  public PeopleEnum(Person[] list)
  {
    _people = list;
  }

  public bool MoveNext()
  {
    position++;
    return (position < _people.Length);
  }

  public void Reset()
  {
    position = -1;
  }

  object 
  {
    get
    {
      return Current;
    }
  }

  public Person Current
  {
    get
    {
      try
      {
        return _people[position];
      }
      catch (IndexOutOfRangeException)
      {
        throw new InvalidOperationException();
      }
    }
  }
}

class App
{
  static void Main()
  {
    Person[] peopleArray = new Person[3]
    {
      new Person("John", "Smith"),
      new Person("Jim", "Johnson"),
      new Person("Sue", "Rabon"),
    };

    People peopleList = new People(peopleArray);
    foreach (Person p in peopleList)
      ( + " " + );

  }
}

/* This code produces output similar to the following:
 *
 * John Smith
 * Jim Johnson
 * Sue Rabon
 *
 */

With the keyword yield, we can create an enumerator in this way:

using System;
using ;

// Simple business object.
public class Person
{
  public Person(string fName, string lName)
  {
     = fName;
     = lName;
  }

  public string firstName;
  public string lastName;
}

// Collection of Person objects. This class
// implements IEnumerable so that it can be used
// with ForEach syntax.
public class People : IEnumerable
{
  private Person[] _people;

  public People(Person[] pArray)
  {
    _people = new Person[];

    for (int i = 0; i < ; i++)
    {
      _people[i] = pArray[i];
    }
  }

  // Implementation for the GetEnumerator method.
  IEnumerator ()
  {
    for (int i = 0; i < _people.Length; i++)
    {
      yield return _people[i];
    }
  }

}


class App
{
  static void Main()
  {
    Person[] peopleArray = new Person[3]
    {
      new Person("John", "Smith"),
      new Person("Jim", "Johnson"),
      new Person("Sue", "Rabon"),
    };

    People peopleList = new People(peopleArray);
    foreach (Person p in peopleList)
      ( + " " + );
  }
}

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.