Click here to Skip to main content
15,881,715 members
Articles / Programming Languages / C#

Foreach On IEnumerable

Rate me:
Please Sign up or sign in to vote.
4.74/5 (15 votes)
6 Aug 2013CPOL4 min read 175.4K   439   28   20
Working of foreach loop.

Introduction

I was working with my team on a regular working day. I was using foreach loop and one of my co-developers asked me if I used the IEnumerable collection variable in a foreach loop constructor. I was aware that foreach loop can apply on the IEnumerable but I was not aware why.

So I searched MSDN for IEnumerable and I got my answer.

I would like to share this with you.

Background

Let me make a few statements as my background for the concept.

  • The IEnumerable<T> interface is a generic interface that provides an abstraction for looping over elements. In addition to providing foreach support, it allows you to use extension methods in the System.Linq namespace.
  • foreach loop is used on a collection that implements the IEnumerable interface.
  • foreach loop returns each element of a collection in the order it is called in the enumeration. 
  • foreach provides in order element access from a collection; it does not accept index and provides access to elements, so eliminates errors caused by incorrect index handling. 
  • IEnumerable is an interface enforcing a rule that we need to implement the GetEnumerator method.
  • GetEnumerator method returns an IEnumerator interface.

Following is the signature for the GetEnumerator method.

C#
public IEnumerator GetEnumerator(void) 

Now we have to implement the IEnumerator interface with the IEnumerable interface in order to accept a return value from the GetEnumerator method.

The IEnumerator interface enforces rules that we need to implement as follows:

  • MoveNext method [public bool MoveNext(void)]
    • Increment collection counter by one
    • If end of collection has been reached, it will return false else return true
  • Reset method [pubic void Reset(void)]
    • Resets the collection index to its initial value of -1
  • Current method [public object Current(void)]
    • Return the current index object from collection

Using the Code

  • Start Microsoft Visual Studio
  • Open File->New->Project->Console Application
  • Give some nice name to console application
  • Open Project->AddClass
  • Give name Employee to it
    C#
    public class Employee
    {
        private int employee_Id;
        private string employee_Name;
    
        public Employee(int employee_Id, string employee_Name)
        {
            this.employee_Id = employee_Id;
            this.employee_Name = employee_Name;
        }
    
        public int Employee_Id { get { 
                                        return employee_Id; 
                                     }
                                 set {
                                       employee_Id=value; 
                                     } 
                                }
    
        public string Employee_Name {
                                        get {
                                              return employee_Name; 
                                            } 
                                        set { 
                                             employee_Name=value; 
                                            } 
                                    }
    }
  • Employee class is a single unit type in the foreach loop.
  • We have exposed two properties Employee_Id and Employee_Name.
  • Now let’s start creating our collection that would be applied on the Foreach loop.
  • Let’s start following statements that we had stated in the background.
  • Create class Employees and implement the IEnumerable and IEnumerator interfaces.
  • We will also create an Employee class array collection and initialize in the constructor by adding an Employee object into the collection of the Employee array.
  • C#
    public class Employees : IEnumerable,IEnumerator
    {
        private Employee[] employeeList;
        private int position = -1;
    
        public Employees()
        {
            employeeList = new Employee[4]{
                        new Employee(1,"Amey"),
                        new Employee(2,"Pushkar"),
                        new Employee(3,"Raju"),
                        new Employee(5,"Vijay")
            };
        }
    
        public object Current
        {
            get
            {
                try
                {
                    return employeeList[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
    
        public bool MoveNext()
        {
            position++;
            return (position < employeeList.Length);
        }
    
        public void Reset()
        {
            position = 0;
        }
    
        public IEnumerator GetEnumerator()
        {
            return (IEnumerator)this;
        }
    } 
  • Let’s move to our Program class that contains the Main method.
  • Let’s use the foreach loop on the Employees class that implements the interfaces IEnumerable and IEnumerator. The Employees class contains an array collection of the Employee class. 
  • Add the following code in the Main method.
  • C#
    static void Main(string[] args)
    {
        var employees = new Employees();
        foreach (Employee employee in employees)
        {
           Console.WriteLine("Employee Id:- "+employee.Employee_Id.ToString()
                              +"\t\t"
                              +"Employee Name:- "+employee.Employee_Name);
        }
    
        Console.ReadLine();
    }
  • Run the project.

You will observe the following output:

Employee Id: - 1     Employee Name: - Amey
Employee Id: - 2     Employee Name: - Pushkar
Employee Id: - 3     Employee Name: - Raju 
Employee Id: - 5     Employee Name: - Vijay  

Code optimization

  1. Yield keyword in C#.
  2. In C#, it is not strictly necessary for a collection class to implement IEnumerable and IEnumerator in order to be compatible with foreach.

    As long as the class has the required GetEnumerator, MoveNext, Reset, and Current members, it will work with foreach.

    Or we can say that: 

    As long as the class has the required GetEnumerator() function that returns an instance that implements either an IEnumerator or IEnumerator<T> interface that enforces to use members MoveNext, Reset, and Current.

    When you use the yield keyword in a statement, you indicate that the method, operator, or get accessor in which it appears is an iterator, i.e., Yield keyword appears in an iterator block.

    When a yield return statement is reached in the iterator method, the current state of the code is retain.

    An expression that evaluates the value to enumerator [i.e., Employee class object] is returned and control moves to the caller. 

    After executing the caller block, control again moves back to the iterator method and restarts from the retain state.

    Based on the above statement, we can optimize our Employees class as below: 

    C#
    public class Employees : IEnumerable
    {
        private Employee[] employeeList;
        private int position = -1;
        public Employees()
        {
            employeeList = new Employee[4]{
                        new Employee(1,"Amey"),
                        new Employee(2,"Pushkar"),
                        new Employee(3,"Raju"),
                        new Employee(5,"Vijay")
            };
        }
    
    
        public IEnumerator GetEnumerator()
        {
            foreach(Employee employee in employeeList)
              yield return employee; 
        }
    }
  3. Foreach is not type safe.
  4. As our foreach statement in main method can be written as:

    C#
    Foreach(int employee… or Foreach (string employee ) 

    So it is not type safe, right? To make it type safe, let’s declare it as var which gets assigned its type during runtime.

    So our main function looks like below:

    C#
    static void Main(string[] args)
    {
        Employees employees = new Employees();
        foreach (var employee in employees)
        {
            Console.WriteLine("Employee Id:- " + 
            (employee as Employee).Employee_Id.ToString()
                               + "\t\t"
                               + "Employee Name:- " + 
                               (employee as Employee).Employee_Name);
        }
        Console.ReadLine();
    } 

Notes

  1. The foreach construct comes from C# 1.0, before generics existed. It worked with untyped collections such as ArrayList or IEnumerable. Therefore, the IEnumerator.Current property that gets assigned to the loop variable would usually be of type object.
  2. C#
    public object Current
    {
        get
        {
            try
            {
                return (Employee)employeeList[position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }
  3. Iterator Pattern falls into the behavioral category. Iterator pattern helps developers to expose a collection element access in a controlled manner. An iterator allows sequential access of elements without exposing the inside code. It protects a collection from errors like error caused by incorrect index handling. When a developer uses a foreach loop he is using the Iterator Pattern knowing or unknowingly.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer Lionbridge
India India
Amey K Bhatkar, a “Microsoft .Net” Web Developer.
I am programmer by will and profession.
I have completed my MCA in 2011 and join software industry.
Presently I am working with Lion Bridge Technologies in Mumbai - India

Comments and Discussions

 
QuestionCan you please help me in my issue and see IENUMERABLE error Pin
Member 1350560312-Dec-21 10:23
Member 1350560312-Dec-21 10:23 
GeneralMy vote of 2 Pin
Paul K T12-Aug-13 7:53
Paul K T12-Aug-13 7:53 
GeneralMy vote of 3 Pin
Jasmine250112-Aug-13 7:26
Jasmine250112-Aug-13 7:26 
GeneralMy vote of 3 Pin
Amir Mohammad Nasrollahi28-Jul-13 19:43
professionalAmir Mohammad Nasrollahi28-Jul-13 19:43 
SuggestionImplementing IEnumerable and IEnumerator on the same class PinPopular
John Brett3-Jun-13 22:41
John Brett3-Jun-13 22:41 
GeneralRe: Implementing IEnumerable and IEnumerator on the same class Pin
Daniele Rota Nodari28-Jul-13 23:23
Daniele Rota Nodari28-Jul-13 23:23 
GeneralMy vote of 1 Pin
tumbledDown2earth30-May-13 1:47
tumbledDown2earth30-May-13 1:47 
GeneralRe: My vote of 1 Pin
Amey K Bhatkar27-Jul-13 6:25
Amey K Bhatkar27-Jul-13 6:25 
QuestionI don't think your foreach statement is type-safe? Pin
George Swan27-May-13 0:38
mveGeorge Swan27-May-13 0:38 
AnswerRe: I don't think your foreach statement is type-safe? Pin
Amey K Bhatkar29-May-13 0:27
Amey K Bhatkar29-May-13 0:27 
GeneralRe: I don't think your foreach statement is type-safe? Pin
George Swan29-May-13 2:40
mveGeorge Swan29-May-13 2:40 
SuggestionIf you use C# 2.0 or above, "yield" is a nice alternative to writing your own enumerator class PinPopular
Andreas Gieriet16-Apr-13 8:39
professionalAndreas Gieriet16-Apr-13 8:39 
GeneralRe: If you use C# 2.0 or above, "yield" is a nice alternative to writing your own enumerator class Pin
Amey K Bhatkar26-May-13 19:35
Amey K Bhatkar26-May-13 19:35 
GeneralRe: If you use C# 2.0 or above, "yield" is a nice alternative to writing your own enumerator class Pin
Andreas Gieriet26-May-13 21:01
professionalAndreas Gieriet26-May-13 21:01 
GeneralRe: If you use C# 2.0 or above, "yield" is a nice alternative to writing your own enumerator class Pin
Amey K Bhatkar27-May-13 5:05
Amey K Bhatkar27-May-13 5:05 
GeneralYour statment on IEnumerable is not fully correct... Pin
Andreas Gieriet16-Apr-13 8:17
professionalAndreas Gieriet16-Apr-13 8:17 
GeneralRe: Your statment on IEnumerable is not fully correct... Pin
Amey K Bhatkar26-May-13 19:42
Amey K Bhatkar26-May-13 19:42 
GeneralRe: Your statment on IEnumerable is not fully correct... Pin
Richard Deeming7-Jun-13 5:41
mveRichard Deeming7-Jun-13 5:41 
GeneralRe: Your statment on IEnumerable is not fully correct... Pin
Andreas Gieriet9-Jun-13 16:35
professionalAndreas Gieriet9-Jun-13 16:35 
GeneralRe: Your statment on IEnumerable is not fully correct... Pin
Mario Z15-Apr-15 0:04
professionalMario Z15-Apr-15 0:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.