Практическое руководство. Доступ к классу коллекции с помощью оператора foreach (Руководство по программированию в C#)

В следующем примере кода показано, как записать неуниверсальный класс коллекции, который можно использовать с оператором foreach. В этом примере определяется класс строкового лексического анализатора.

Примечание

Этот пример представляет рекомендованный способ, только если использование универсального класса коллекции невозможно.Для примера реализации типобезопасного универсального класса коллекции, поддерживающего тип IEnumerable, см. раздел Итераторы (C# и Visual Basic).

В примере следующий сегмент кода использует класс Tokens для разделения предложения "Это предложение-образец." на токены с помощью разделителей " " и "-". Затем код отображает эти токены с помощью оператора foreach.

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

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

Пример

По сути, класс Tokens использует массив для хранения токенов. Поскольку массивы реализуют типы IEnumerator и IEnumerable, пример кода может использовать методы перечисления массивов (GetEnumerator, MoveNext, Reset и Current) вместо определения их в классе Tokens. Определения методов содержатся в примере для уточнения способа их определения и предназначения каждого из них.

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.  
*/

В языке C# классу коллекции необязательно реализовывать IEnumerable и IEnumerator для совместимости с foreach. Если в классе содержатся обязательные члены GetEnumerator, MoveNext, Reset и Current, он будет работать с оператором foreach. Пропуск интерфейсов имеет преимущество, поскольку позволяет определить тип возврата для Current, который более специфичен, чем тип Object. Это повышает типобезопасность.

Например, измените следующие строки в предыдущем примере.

// 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
        {   }
    }
 }

Поскольку Current возвращает строку, компилятор может определить, когда используется несовместимый тип в операторе foreach, как показано в следующем примере.

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

Недостатком пропуска типов IEnumerable и IEnumerator является то, что класс коллекции больше не может взаимодействовать с операторами foreach или эквивалентными операторами из других языков среды CLR.

См. также

Ссылки

Массивы (Руководство по программированию на C#)

System.Collections.Generic

Основные понятия

Руководство по программированию на C#

Другие ресурсы

Справочник по C#

Коллекции (C# и Visual Basic)