How to: Access a Collection Class with foreach (C# Programming Guide)

The following code example illustrates how to write a non-generic collection class that can be used with foreach. The example defines a string tokenizer class.

Note

This example represents recommended practice only when you cannot use a generic collection class. For an example of how to implement a type-safe generic collection class that supports IEnumerable<T>, see How to: Create an Iterator Block for a Generic List (C# Programming Guide).

In the example, the following code segment uses the Tokens class to break the sentence "This is a sample sentence." into tokens by using ' ' and '-' as separators. The code then displays those tokens by using a foreach statement.

Tokens f = new Tokens("This is a sample sentence.", new char[] {' ','-'});

// Display the tokens.
foreach (string item in f)
{
    System.Console.WriteLine(item);
}

Example

Internally, the Tokens class uses an array to store the tokens. Because arrays implement IEnumerator and IEnumerable, the code example could have used the array's enumeration methods (GetEnumerator, MoveNext, Reset, and Current) instead of defining them in the Tokens class. The method definitions are included in the example to clarify how they are defined and what each does.

using System.Collections;

// Declare the Tokens class. The class implements the IEnumerable interface.
public class Tokens : IEnumerable
{
    private string[] elements;

    Tokens(string source, char[] delimiters)
    {
        // The constructor parses the string argument into tokens.
        elements = source.Split(delimiters);
    }

    // The IEnumerable interface requires implementation of method GetEnumerator.
    public IEnumerator GetEnumerator()
    {
        return new TokenEnumerator(this);
    }


    // Declare an inner class that implements the IEnumerator interface.
    private class TokenEnumerator : IEnumerator
    {
        private int position = -1;
        private Tokens t;

        public TokenEnumerator(Tokens t)
        {
            this.t = t;
        }

        // The IEnumerator interface requires a MoveNext method.
        public bool MoveNext()
        {
            if (position < t.elements.Length - 1)
            {
                position++;
                return true;
            }
            else
            {
                return false;
            }
        }

        // The IEnumerator interface requires a Reset method.
        public void Reset()
        {
            position = -1;
        }

        // The IEnumerator interface requires a Current method.
        public object Current
        {
            get
            {
                return t.elements[position];
            }
        }
    }


    // Test the Tokens class.
    static void Main()
    {
        // Create a Tokens instance.
        Tokens f = new Tokens("This is a sample sentence.", new char[] {' ','-'});

        // Display the tokens.
        foreach (string item in f)
        {
            System.Console.WriteLine(item);
        }
    }
}
/* Output:
    This
    is
    a
    sample
    sentence.  
*/

In C#, it is not necessary for a collection class to implement IEnumerable and IEnumerator to be compatible with foreach. If the class has the required GetEnumerator, MoveNext, Reset, and Current members, it will work with foreach. Omitting the interfaces has the advantage of enabling you to define a return type for Current that is more specific than Object. This provides type safety.

For example, change the following lines in the previous example.

// Change the Tokens class so that it no longer implements IEnumerable.
public class Tokens
{
    // . . .

    // Change the return type for the GetEnumerator method.
    public TokenEnumerator GetEnumerator()
    {   }

    // Change TokenEnumerator so that it no longer implements IEnumerator.
    public class TokenEnumerator
    {
        // . . .

        // Change the return type of method Current to string.
        public string Current
        {   }
    }
 }

Because Current returns a string, the compiler can detect when an incompatible type is used in a foreach statement, as shown in the following code.

// Error: Cannot convert type string to int.
foreach (int item in f)  

The disadvantage of omitting IEnumerable and IEnumerator is that the collection class is no longer interoperable with the foreach statements, or equivalent statements, of other common language runtime languages.

See Also

Reference

Arrays (C# Programming Guide)

Collection Classes (C# Programming Guide)

System.Collections.Generic

Concepts

C# Programming Guide

Other Resources

C# Reference

Change History

Date

History

Reason

March 2011

Revised to enhance clarity.

Information enhancement.