SoFunction
Updated on 2025-03-06

Detailed explanation of enumerable types in C#

Enumeration is the process of iterating over data items in a collection.

Most of the collections we often use actually implement the enumeration interfaces IEnumerable and IEnumerator interfaces, so that we can use foreach iteration. Some are interfaces that contain some abstraction of enumeration details: the ArrayList type has indexes, BitArray has Get methods, and hash tables and dictionaries have keys and values... In fact, they have all implemented the IEnumerable and IEnumerator interfaces. Therefore, all collections and arrays can be defined using the IEnumerable or IEnumerable<T> interface.

 IEnumerable lists1 = new int[] { 3, 4, 5 };
      foreach(var val in lists1)
      {
        (val);
      }
      IEnumerable<int> lists2=new int[]{1,2,3};
      foreach(var val in lists2)
      {
        (val);
      }

Let’s explain below: Defining the enumerable type yourself (simply put, it is a collection that defines it yourself and can perform foreach iteration):

Because enumerations are very beneficial and can eliminate many errors, it is beneficial to implement a certain standard. This standard is the IEnumerable and IEnumerator interfaces. It must be implemented to use foreach iteration to truly be considered a self-defined and sound collection.

The enumable types we establish ourselves must implement the IEnumerable and IEnumerator interfaces (in fact, both have a generic implementation).

The IEnumerable interface contains a method that returns an enumerator object. The enumerator object implements the IEnumerator interface (in fact, it can be considered that the object of the class that inherits and implements the IEnumerator interface is an enumerator object), and can be used to iterate.

The following are the definitions of the two interfaces (the system has already defined it):

 public interface IEnumerable
  {
    IEnumerator GetEnumerator();
  }

This interface has only one GetEnumerator method, which returns an enumerator for enumerating elements in the collection.

 public interface IEnumerator
  {
    object Current { get; };//Current property returns the current element of the collection    bool MoveNext();    //Move the enum to the next one    void Reset();     //Bring the enum to the beginning  }

Any class object that inherits and implements the above interface is an enumerator. It can be enumerated using the above three methods, which is very safe. However, you need to write the implementation process yourself in the code that inherits the interface.

The general situation is that the enumerator is part of the enumeration pattern and is usually implemented as a nested class (inheriting IEnumerable) of the enumeration type (inheriting IEnumerable). The advantage of nested classes is that they can access private members of external classes without breaking the encapsulation principle.

Let's define an enum type ourselves, the code is as follows:

public class SimpleCollection :IEnumerable
  {
    //Define an array of fields    private object[] array;

    //Define a constructor    public SimpleCollection(object []items)
    {
      array = items;
    }
    //Implementing the GetNumerator method of IEnumerable interface This method returns an instance of a class inheriting the IEnumerator interface    public  IEnumerator GetEnumerator()
    {
      return  new Enumerator(array);
    }
    //Define a nested class to inherit the interface of IEnumerator    public class Enumerator : IEnumerator
    {
      //Define a tag field      private int flag;
      //Define an array of fields      private object[] elements = null;
      //Define a constructor      public Enumerator(object []items)
      {
        elements = items;
        flag = -1; //Initialize the marker bit        
        //The following method can also be used        //elements = new object[];
        //(items, elements, );//This static method is used to copy elements in an array to another array      }
      //Implement the Current property of the IEnumerator interface; this property returns the current element of the collection and is read-only      public object Current
      {
        get
        {
          if (flag &gt;  - 1) throw new InvalidOperationException("The enumeration has ended");
          else if (flag &lt; 0) throw new InvalidOperationException("The enumeration has not started yet");
          else return elements[flag];
        }
      }
      //MoveNext method that implements the IEnumerator interface moves the enumeration to the next one      public bool MoveNext()
      {
        ++flag;
        if (flag &gt; ( - 1)) return false;
        else return true;
      }
      //Implement the Reset method of the IEnumerator interface to return the enumeration to the beginning      public void Reset()
      {
        flag = -1;
      }
    }

Here is how to use enumeration types when delaying:

//Let's look at the usage of enum types      SimpleCollection collection = new SimpleCollection(new object[]{1,2,3,4,5});

      //How to use      //Interface variable name = instance of class inherited from the interface      IEnumerator enumrator = ();

      while(())
      {
        
        ();
      }
      ();

 SimpleCollection simple = new SimpleCollection(new object[] { 1, 2, 3, 4, 5, 6 });
      IEnumerator enumerator = ();
      while(())
      {
        ();
      }
      //The most important thing is that after implementing those two interfaces, we can use foreach to iterate on our collection. See below      foreach(var s in simple)
      {
        (s);
      }

The following are generic implementations of the two interfaces:

The first thing to note is:

<T>Interface inherits from IEnumerable        Both have the same interface, so the generic and non-generic versions of the GetEumerator method must be implemented.

<T>Interface inherits from IEnumerator and IDisposable. It requires multiple current attributes and IDisposable interfaces that implement generic and non-generic versions of Current attributes and Dispose methods.

The code is as follows:

//// Create an enumerable pan-type below  //First of all, this type must inherit the IEnumerable<T> interface  //Because the IEnumerable<T> interface inherits the IEnumerable interface, it is necessary to implement both generic and non-generic GetEnumerator methods.  public class SimpleCollection&lt;T&gt; : IEnumerable&lt;T&gt;
  {
    private T[] array;
    public SimpleCollection(T[] items)
    {
      array = items;
     
    }
    //Implementing the GetNumerator method of IEnumerable<T> interface This method returns an instance of a class inheriting the IEnumerator interface    public IEnumerator&lt;T&gt; GetEnumerator()
    {
      return new Enumerator&lt;T&gt;(array);//This step needs to be taken seriously    }
    //To avoid confusion, non-generic interfaces are explicitly implemented here    IEnumerator ()
    {
      return new Enumerator&lt;T&gt;(array);//This step needs to be taken seriously    }

    //Define a nested class to inherit the interface of IEnumerator<T>    //The IEnumerator<T> interface inherits from IDisposable and IEnumerator interfaces    //The only member of this interface is the Current property, but it also needs to implement its non-generic version!  !  !    //In addition, two methods of implementing IDisposable's Dispose method and IEnumerator need to be two methods    public class Enumerator&lt;_T&gt; : IEnumerator&lt;_T&gt;
    {
      private int flag;
      private _T[] elements = null;
      public Enumerator(_T[] items)
      {
        elements = items;
        flag = -1;
      }
      //Implement the Current property of the IEnumerator<T> interface; This property returns the current element of the collection and is read-only      public _T Current
      {
        get
        {
          if (flag &gt;  - 1) throw new InvalidOperationException("The enumeration has ended");
          else if (flag &lt; 0) throw new InvalidOperationException("The enumeration has not started yet");
          else return elements[flag];
        }
      }
      //To avoid confusion, display the Current attribute that implements the IEnumerator interface      object 
      {
        get { return Current; } //Return directly to the above generic attributes.      }

      //Dispose method that implements IDisposable interface supports deterministic garbage collection. Set the status of the enumerator to after, that is, set the mark bit to the maximum index +1      public void Dispose()
      {
        flag =  + 1;
      }

      //MoveNext method that implements the IEnumerator interface moves the enumeration to the next one      public bool MoveNext()
      {
        ++flag;
        if (flag &gt; ( - 1)) return false;
        else return true;
      }
      //Implement the Reset method of the IEnumerator interface to return the enumeration to the beginning      public void Reset()
      {
        flag = -1;
      }
    }

How to use:

   SimpleCollection<string> colletion = new SimpleCollection<string>(new string[] { "ranran", "Huaran" });
      IEnumerator<string> enumorator = ();
      while(())
      {
        ();
      }
      foreach(var v in colletion)
      {
        (v);
      }
      ();

You can also use iterators directly:
Using iterators is another solution to fully implement the above two interfaces, which is the easiest and readable method.

Moreover, using iterators can easily and quickly set up various enumeration situations such as double iteration, reverse iteration, temporary collection and responsible iteration, etc. It is simpler than the above implementation.

The keyword of iteration is yield needs to rely on an iterator block (note that it is loop + yield return, or yiled break)

 public class MyCollection:IEnumerable
  {
    private object[] array;
    public MyCollection(object []items)
    {
      array = items;
    }
    public IEnumerator GetEnumerator() //Implementation can be done by relying on the compiler    {
      //foreach (object v in array)
      //{
      //  yield return v;
      //}

      //The keyword is yield not foreach We can also implement it according to the following method      for(int i=0;i&lt;;i++)
      {
        yield return array[i];
      }
      //Of course other while loops are also OK.  .    }
  }
//accomplish:MyCollection collection = new MyCollection(new object[] { 1, 2, 3 });
      foreach(var v in collection)
      {
        (v);
      }

You can set the iteration situation yourself:

   public class MyCollection2:IEnumerable
  {
     private object[] array;
     public MyCollection2(object []items)
    {
      array = items;
    }
    //The implementation of iteration can be set in the iterator block, that is, how many elements are iterated in specific iterations.    //For example, we only want to iterate over 4 elements    public IEnumerator GetEnumerator()
     {
       int count = 0;//Design a marker bit      foreach(object item in array)
      {
        ++count;
        yield return item;
        if (count == 4) yield break; //break keyword Exit iteration In fact, iteration is a loop in implementation. It is reasonable to use break to jump out.      }
     }
   
  }


//////
 MyCollection2 collection2 = new MyCollection2(new object[]{4,5,6,7,8});
      //It will only output 4, 5, 6, 7      foreach (var v in collection2)
      {
        (v);
      }

Double iteration:

 /// &lt;summary&gt;
  /// The following demonstrates double iteration, that is, elements in two sets can be iterated at once  /// &lt;/summary&gt;
  public class MyColletion3:IEnumerable
  {
    private object[] List1;
    public string[] List2;
    public MyColletion3(object []items1,string []items2)
    {
      this.List1 = items1;
      this.List2 = items2;
    }
    //Double iteration below    public IEnumerator GetEnumerator()
    {
      //Key Code      for(int index=0;index&lt;(&gt;?:);index++)
      {
        yield return List1[index];
        yield return List2[index];
      }
    }
  }
////////
 MyColletion3 collection3 = new MyColletion3(new object[] { 1, 2, 3, 5.5 }, new string[] { "RanRan", "Chengdu", "Sichuan" });
      foreach(var v in collection3)
      {
        (v);
      }
      //The iteration result is 1 RanRan 2 Chengdu 3 Sichuan

Reverse Iteration: Relying on Reverse attribute

  /// &lt;summary&gt;
  /// The following demonstrates reverse iteration. To put it bluntly, iteration starts from the back. The reverse iterator is implemented in the Reverse property  /// &lt;/summary&gt;
  public class MyColletion4:IEnumerable
  {
    private object[] items;
    public MyColletion4(object []temps)
    {
       = temps;
    }
    //General forward iteration    public IEnumerator GetEnumerator()
    {
      for(int index=0;index&lt;;index++)
      {
        yield return items[index];
      }
    }
    //Implement reverse iteration    public IEnumerable Reverse //Note that IEnumerable object is returned    {
      get
      {
        for (int index =  - 1; index &gt; -1; index--)
        {
          yield return items[index];
        }
      }
    }
  }
////
 MyColletion4 collection4 = new MyColletion4(new object[] { 1, 2, 3, 4 });
      foreach (var v in collection4)
      {
        (v);
      }
      //Reverse iteration      foreach(var v in )
      {
        (v);
      }
      //The iteration result is 4 3 2 1

Of course, there is also a temporary collection. By the way, there are many solutions for iteration and enumeration implementation. A method that returns IEnumerable plus iterator blocks is also an iterative collection.

See the following code for details

 //There is another simplest iteration, which is a method that returns an IEnumerable object. Write an iterator in this method.    //Add a temporary collection here. The key is to see how to write the code (using the date of the enumerated current month as a column)    public static IEnumerable GetMonthDate()
    {
      DateTime dt = ;
      int currentMonth = ;
      while(currentMonth==)
      {
        string temp = () + "/" + ();
        dt = (1);
        yield return temp;
      }
    }

///accomplishforeach(var v in GetMonthDate())
      {
        (v);
      }

Here, as a novice, I will summarize the meaning of enumerable types and interfaces for myself:

Enumerable types (sets & arrays, etc.):

In actual development, you can define some types similar to collections by yourself. It is inconvenient to use general while and for loops to access elements of this type. We need to define an enumerator by ourselves.

Enumeration type (inherited from the IEnumerable interface): includes a collection element and an enumerator.

The enumerator is a nested class in the enumeration type (inheriting the IEnumerator interface): see the above for specific implementation.

/////// This allows custom enumerable types to implement foreach iteration.

Of course, it can also be directly used to implement the above two interfaces. //////////////

Interface: It is a standard, which gives a kind of constraint and guidance, and we need to write code to implement it. Although it seems to have taken one shot many times, it is very convenient to use the class instance later.

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.