Доброе время суток. Пришло время для продолжения рассказа о цикле foreach
в языке программирования C#. Из первой статьи вы уже немного знаете как пользоваться данным циклом, чем он отличается от привычного for. Так же были предложены примеры кода для обхода элементов коллекции List (System.Collections.Generic) и элементов массива. Но не был дан ответ, к каким же объектам можно
применять цикл foreach, а к каким нет.
Итак, цикл foreach можно применять только к объектам, реализующим интерфейс IEnumerable (пространство имен — System.Collections) или IEnumerable<T>> (пространство имен — System.Collections.Generic), в зависимости от коллекции объектов. Не путайте два эти интерфейса, первый надо реализовывать, когда у вас в качестве контейнера элементов допустим массив, второй — допустим, когда у вас контейнер List. Почему именно эти интерфейсы и зачем они нужны? Дело в том, что цикл foreach — это всего лишь достаточно удобная оболочка, которая внутри себя вызывает определенные свойства и методы самого объекта. Эти методы, свойства и необходимо реализовать, если мы хотим, чтобы объект можно было использовать в цикле foreach.
Чтобы стало понятнее, давайте рассмотрим пример:
-
-
using System.Collections;
-
public class TestCollection : IEnumerable
-
{
-
private int[] array;
-
public TestCollection(int[] argList)
-
{
-
array = argList;
-
}
-
public IEnumerator GetEnumerator()
-
{
-
return new TestEnumerator(this);
-
}
-
private class TestEnumerator : IEnumerator
-
{
-
int position = —1;
-
TestCollection collection;
-
}
-
public TestEnumerator(TestCollection col)
-
{
-
collection = col;
-
}
-
public bool MoveNext()
-
{
-
if (position < collection.array.Length — 1)
-
{
-
position++;
-
return true;
-
}
-
else
-
{
-
return false;
-
}
-
}
-
public void Reset()
-
{
-
position = —1;
-
}
-
object IEnumerator.Current
-
{
-
get { return collection.array[position]; }
-
}
-
}
-
}
-
Как видно из кода класс TestCollection реализует интерфейс IEnumerator,
для этого переопределяем метод GetEnumerator(). Данный метод возвращает объект (реализующий IEnumerator),
знающий как правильно обходить коллекцию. Класс TestEnumerator реализует IEnumerator,
в котором есть метод Reset() — он сбрасывает счетчик в начальную позицию, метод MoveNext() — он
сдвигает счетчик, и возвращает true — если еще мы не вышли за пределы коллекции, т.е. двигаться
к следующему элементу можно, и возвращает false — в противном случае.
Так же есть свойство Current — оно возвращает текущий элемент коллекции.
Отсюда не сложно догадаться как работает на цикл foreach изнутри. В начале вызывается
метод Reset(), затем после каждой итерации вызывается MoveNext() и свойство нужно
непосредственно в самом цикле для доступа к текущему элементу. Когда MoveNext() возвращает — false,
то мы выходим из цикла foreach.
Далее небольшой пример кода по использованию нашего класса TestCollection в цикле foreach:
-
-
using System.Collections;
-
int sum = 0;
-
int []ar = {1, 2, 5, 3};
-
TestCollection col = new TestCollection(ar);
-
foreach(int value in col)
-
{
-
sum += value;
-
}
-
В общем как вы уже успели заметили цикл foreach не так и прост изнутри, как может показаться
на первый взгляд. И за данную легкость использования приходится заплатить производительностью. Но это не должно
вас пугать и для большинства задач foreach подходит отлично. И есть еще один ньюанс, про который лучше не забывать и стоит
обратить внимание при использовании цикла foreach…Не стоит изменять коллекцию элементов
внутри цикла, так как это может нарушить цикл.
На этом пожалуй всё о цикле foreach, если дойдут руки возможно будет и третья часть по данной теме.