Share via


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

The following code sample illustrates how to write a non-generic collection class that can be used with foreach. The class is a string tokenizer, similar to the C run-time function strtok.

Note

his example represents recommended practice only in cases where you cannot use a generic collection class. Generics are supported in version 2.0 and later of the C# language and the .NET Framework. For an example of how to implement a type-safe generic collection class that supports IEnumerable<T> and thereby avoids the issues discussed below, see How to: Create an Iterator Block for a Generic List (C# Programming Guide).

In the following example, Tokens breaks the sentence "This is a sample sentence." into tokens using ' ' and '-' as separators, and enumerates those tokens with the foreach statement:

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

foreach (string item in f)
{
    System.Console.WriteLine(item);
}

Internally, Tokens uses an array, which implements IEnumerator and IEnumerable itself. The code example could have used the array's enumeration methods as its own, but that would have defeated the purpose of this example.

In C#, it is not strictly necessary for a collection class to inherit from 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. Omitting the interfaces has the advantage of allowing you to define the return type of Current to be more specific than object, thereby providing type-safety.

For example, starting with the sample code above, change the following lines:

// No longer inherits from IEnumerable:
public class Tokens  
// Doesn't return an IEnumerator:
public TokenEnumerator GetEnumerator()  
// No longer inherits from IEnumerator:
public class TokenEnumerator  
// Type-safe: returns string, not object:
public string Current  

Now, because Current returns a string, the compiler can detect when an incompatible type is used in a foreach statement:

// Error: cannot convert 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 equivalents, of other common language runtime-compatible languages.

You can have the best of both worlds, type-safety within C# and interoperability with other common language runtime-compatible languages, by inheriting from IEnumerable and IEnumerator and using explicit interface implementation as demonstrated in the following example.

Example

using System.Collections;

// Declare the Tokens class:
public class Tokens : IEnumerable
{
    private string[] elements;

    Tokens(string source, char[] delimiters)
    {
        // Parse the string into tokens:
        elements = source.Split(delimiters);
    }

    // IEnumerable Interface Implementation:
    //   Declaration of the GetEnumerator() method 
    //   required by IEnumerable
    public IEnumerator GetEnumerator()
    {
        return new TokenEnumerator(this);
    }


    // Inner class implements IEnumerator interface:
    private class TokenEnumerator : IEnumerator
    {
        private int position = -1;
        private Tokens t;

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

        // Declare the MoveNext method required by IEnumerator:
        public bool MoveNext()
        {
            if (position < t.elements.Length - 1)
            {
                position++;
                return true;
            }
            else
            {
                return false;
            }
        }

        // Declare the Reset method required by IEnumerator:
        public void Reset()
        {
            position = -1;
        }

        // Declare the Current property required by IEnumerator:
        public object Current
        {
            get
            {
                return t.elements[position];
            }
        }
    }


    // Test Tokens, TokenEnumerator
    static void Main()
    {
        // Testing Tokens by breaking the string into tokens:
        Tokens f = new Tokens("This is a sample sentence.", new char[] {' ','-'});

        foreach (string item in f)
        {
            System.Console.WriteLine(item);
        }
    }
}

Output

This
is
a
sample
sentence.

See Also

Reference

System.Collections.Generic

Concepts

C# Programming Guide
Arrays (C# Programming Guide)
Collection Classes (C# Programming Guide)

Other Resources

C# Reference