IEnumerable совместимость со старым кодом

228
03 ноября 2017, 08:06

Интереса ради начал изучать внутреннее устройство foreach. Наткнулся на такой паттерн, который создан, чтобы была совместимость из старого кода:

class Test : IEnumerable<T>
{
    public IEnumerable<T> GetEnumerator()
    {
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
    }
}

Так как мы реализуем generic интерфейс, то должны реализовать и методы из IEnumerable, так как generic интерфейс наследует его. Как я понял, при написании обычного foreach в текущих версиях языка, будет вызван неявный метод (который первый), исходя из понятия утиной типизации. Вопрос: почему в старых версиях языка (до generic'ов) будет вызван метод с явной реализацией? Возможно, там такая реализация (с привидением к non-generic интерфейсу)?

IEnumerator enumerator = ((IEnumerable)TestInstance).GetEnumerator();
...

Или я что-то неправильно понимаю?

Answer 1

Вы задаёте вопрос о C# 1.x, поскольку начиная с версии 2.0 C# уже содержит обобщения.

Посмотрим на спецификацию C# 1.0. Нас интересует раздел 8.8.4 The foreach statement. Согласно этому разделу, используется следующая последовательность проверок:

  1. Если выражение, по которому происходит итерация, поддерживает паттерн итерации (то есть, содержит публичный метод GetEnumerator, возвращающий объект, обладающий методом MoveNext и свойством Current), то используется этот паттерн (даже если интерфейс IEnumerable тоже поддерживается).
  2. В противном случае, если поддерживается интерфейс IEnumerable, то он используется.
  3. В противном случае происходит ошибка компиляции.

В вашем случае, когда метод IEnumerable.GetEnumerator() объявлен через явную имплементацию интерфейса, первое условие не выполняется: метод IEnumerable.GetEnumerator не публичный. (А метода public IEnumerable<T> GetEnumerator() вовсе нет, раз наша версия языка не поддерживает обобщения.) Поэтому работает второе условие.

Кстати, в современных версиях языка просто перед второй проверкой выполняется дополнительная проверка на обобщённый IEnumerable<T>. Но на первом месте в любом случае «утиная» проверка.

READ ALSO
Зачем нам разные способы сериализации [требует правки]

Зачем нам разные способы сериализации [требует правки]

Зачем сделали разные способы сохранения сериализированных данных? Например зачем нам сериализация в формате XML или SOAP или Бинарная сериализацияЕсли...

243
Удалить строки из тхт, но оставить одну

Удалить строки из тхт, но оставить одну

В предыдущих вопросах, я спрашивал про строки, сейчам появилась нужда удалить все строки, равны или нет, не важно, но чтобы оставить одну

290
Применение SkipWhile в LINQ

Применение SkipWhile в LINQ

Есть коллекция объектов, необходимо удалить из неё некоторые объекты отвечающие условиям (Например если свойство коллекции ID=5 то все объекты...

164
Список из двух и более значений

Список из двух и более значений

Как создать в классе список, который имеет несколько значений? Грубо говоря, сделать что-то, на подобие:

207