В этой статье мы коротко пройдемся по особенностям foreach. Первый момент вы скорее всего знаете, второй момент вы скорее всего не знаете.
Предыдущая статья об Array
На собеседованиях часто спрашивают — «Что необходимо сделать что бы ваш класс работал с
Запоминать эти методы не обязательно, если подсунуть свой неправильный класс в
Тестовый
Неправильный контейнер:
Ошибка компилятора:
Добавим метод
Правильный контейнер:
Неправильный энумератор:
Ошибка компилятора:
Добавим метод
Правильный энумератор:
Теперь компилятор все устраивает.
Примечание:
Свойство
На собеседовании встречаются вопросы про
Энумератор с
Теперь при запуске примера мы увидим строчку «Dispose» в консоли.
Для тех кому интересно, вот код который генерит компилятор для нашего случая:
Всем спасибо за внимание!
Предыдущая статья об Array
Первый момент
На собеседованиях часто спрашивают — «Что необходимо сделать что бы ваш класс работал с
foreach?». Ответ на этот вопрос обычно звучит так — «Реализовать IEnumerable». Ответ этот правильный, но не полный. В принципе, этого ответа на собеседовании достаточно и я ни разу не встречал чтобы кто то считал его неправильным. На самом деле, foreach использует «утиную типизацию». Для того чтобы наш класс работал в foreach достаточно иметь метод GetEnumerator возвращающий нечто имеющее метод MoveNext и свойство Current.Запоминать эти методы не обязательно, если подсунуть свой неправильный класс в
foreach компилятор честно подскажет чего конкретно не хватает в этом классе.Примеры
Тестовый
foreach:class Program { static void Main(string[] args) { var container = new Container(); foreach (var item in container) { } } }
Неправильный контейнер:
public class Container { }
Ошибка компилятора:
foreach statement cannot operate on variables of type 'Container' because 'Container' does not contain a public definition for 'GetEnumerator'Добавим метод
GetEnumerator в контейнер и класс энумератора.Правильный контейнер:
public class Container { public Enumerator GetEnumerator() { return new Enumerator(); } }
Неправильный энумератор:
public class Enumerator { }
Ошибка компилятора:
foreach requires that the return type 'Enumerator' of 'Container.GetEnumerator()' must have a suitable public MoveNext method and public Current propertyДобавим метод
MoveNext и свойство Current в энумератор.Правильный энумератор:
public class Enumerator { public bool MoveNext() { return false; } public object Current { get { return null; } } }
Теперь компилятор все устраивает.
Примечание:
Свойство
Current может возвращать любой тип, как ref type так и value type. Собственно это и стало причиной использования «утиной типизации», во времена когда не было generics, для избежания ненужных boxing и unboxing.Второй момент
На собеседовании встречаются вопросы про
IDisposable и помимо общих вопросов про ручное управление ресурсами есть вопрос про то, в каких случаях компилятор может автоматически вызывать метод Dispose. Ответ все мы знаем — Dispose вызывается автоматически при использовании оператора using(). Ответ этот правильный, но неполный! Метод Dispose может вызывается в двух случаях, помимо using(), он вызывается в foreach для энумератора, если энумератор реализует IDisposable.Примеры
Энумератор с
Dispose:using System; public class Enumerator : IDisposable { public bool MoveNext() { return false; } public object Current { get { return null; } } public void Dispose() { Console.WriteLine("Dispose"); } }
Теперь при запуске примера мы увидим строчку «Dispose» в консоли.
Для тех кому интересно, вот код который генерит компилятор для нашего случая:
Container container = new Container(); Enumerator enumerator = container.GetEnumerator(); try { while (enumerator.MoveNext()) { var element = enumerator.Current; // содержимое foreach } } finally { IDisposable disposable = enumerator as IDisposable; if (disposable != null) disposable.Dispose(); }
Всем спасибо за внимание!
