Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.79/14: Рейтинг темы: голосов - 14, средняя оценка - 4.79
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18

Непонятный код из примера с сайта MSDN. Реализация IEnumerable

31.08.2020, 13:03. Показов 3350. Ответов 28

Студворк — интернет-сервис помощи студентам
Приветствую, друзья.
Изучал статью по интерфейсу IEnumerator на сайте MSDN.
https://docs.microsoft.com/ru-... etcore-3.1

И в примере к статье увидел, очень заинтриговавший меня, код в пользовательском классе-коллекции, реализующем интерфейс IEnumerable:
C#
1
2
3
4
5
6
7
8
9
IEnumerator IEnumerable.GetEnumerator()
{
    return (IEnumerator) GetEnumerator();
}
 
public PeopleEnum GetEnumerator()
{
    return new PeopleEnum(_peolpe);
}
Подскажите, пожалуйста, зачем так сделано? Зачем явная реализация GetEnumerator()? Для чего в ней явное привидение к типу (IEnumerator), если возвращаемый тип возвращаемого объекта - PeopleEnum и так реализует IEnumerator? Зачем сделано отдельное пользовательское определение GetEnumerator()? Ведь можно же вместо этого кода сделать так:
C#
1
2
3
4
public IEnumerator GetEnumerator()
{
    return new PeopleEnum(_people);
}
И все будет так же работать.

Я вполне понимаю, что эти примеры пишут довольно опытные программисты и сделали так по какой то, еще не известной мне, причине. Мне очень интересна эта причина. Поделитесь, пожалуйста, кто знает.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
31.08.2020, 13:03
Ответы с готовыми решениями:

Msdn IEnumerable и MVC# 2013 разница в синтаксисе
public class Person { public Person(string fName, string lName) { this.firstName =...

Непонятный момент с IEnumerable
как это объяснить? неявное наследование от IEnumerable? using System; using System.Collections; class SColl//не наследуемся от...

Ошибка при использовании Tooltip из примера с MSDN
Добрый день. Мне нужно сделать всплывающую подсказку для textBox. С++VS2008 WinForms. нашел это...

28
Пора на C++?
 Аватар для TrickyBestia
370 / 264 / 99
Регистрация: 10.04.2020
Сообщений: 1,275
31.08.2020, 13:08
Цитата Сообщение от loyalist28 Посмотреть сообщение
Зачем сделано отдельное пользовательское определение GetEnumerator()? Ведь можно же вместо этого кода сделать так:
А если вы нужен не IEnumerator, а именно PeopleEnum? Использовать приведение типов? Некрасиво.
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,234
31.08.2020, 13:15
loyalist28,
Если вы глянете на интерфейс IEnumerable<T>, то увидите следующее:
C#
1
2
3
4
5
6
public interface IEnumerable<out T> : IEnumerable
  {
    /// <summary>Returns an enumerator that iterates through the collection.</summary>
    /// <returns>An enumerator that can be used to iterate through the collection.</returns>
    new IEnumerator<T> GetEnumerator();
  }
То есть если вы реализуете типизированную версию интерефейса, то надо реализовать еще и IEnumerable.
Поскольку IEnumerable нас тут не особо интересует, делаем его приватным.

Цитата Сообщение от loyalist28 Посмотреть сообщение
Зачем сделано отдельное пользовательское определение GetEnumerator()
UPD: Отдельное - для реализации типизированной версии интерфейса. + DRY принцип. Зачем дублировать код?
0
Администратор
Эксперт .NET
 Аватар для OwenGlendower
18304 / 14228 / 5368
Регистрация: 17.03.2014
Сообщений: 28,900
Записей в блоге: 1
31.08.2020, 13:35
Цитата Сообщение от loyalist28 Посмотреть сообщение
Ведь можно же вместо этого кода сделать так:
C#
1
2
3
4
public IEnumerator GetEnumerator()
{
    return new PeopleEnum(_people);
}
И все будет так же работать.
Вот именно что не будет. Это приведет к ошибке компиляции "CS0111 Type 'People' already defines a member called 'GetEnumerator' with the same parameter types".
0
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
31.08.2020, 13:43  [ТС]
Цитата Сообщение от IamRain Посмотреть сообщение
То есть если вы реализуете типизированную версию интерефейса, то надо реализовать еще и IEnumerable.
В том то и дело, что реализуем не типизированную версию интерфейса. Посмотрите внимательнее код.

Добавлено через 1 минуту
Цитата Сообщение от OwenGlendower Посмотреть сообщение
Вот именно что не будет. Это приведет к ошибке компиляции "CS0111 Type 'People' already defines a member called 'GetEnumerator' with the same parameter types".
Все работает, я проверил. Уберите явную реализацию GetEnumerator() и пользовательское определение и замените представленным мной вариантом.

Добавлено через 1 минуту
Цитата Сообщение от TrickyBestia Посмотреть сообщение
А если вы нужен не IEnumerator, а именно PeopleEnum? Использовать приведение типов? Некрасиво.
Ну как вариант. Думал об этом, но показалось слишком банальным )
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
31.08.2020, 13:44
Лучший ответ Сообщение было отмечено loyalist28 как решение

Решение

Цитата Сообщение от loyalist28 Посмотреть сообщение
зачем так сделано? Зачем явная реализация GetEnumerator()?
Чтобы можно было использовать свойство Current, возвращающее Person вместо object, и избежать каста.
Пример не самый лучший из-за того, что PeopleEnum — класс; обычно такое делается в целях оптимизации: чтобы использовать значимые типы и не плодить мусор в куче при каждом переборе.
Если сделать PeopleEnum struct'ом, то разница будет очень заметна.

Цитата Сообщение от loyalist28 Посмотреть сообщение
Для чего в ней явное привидение к типу (IEnumerator), если возвращаемый тип возвращаемого объекта - PeopleEnum и так реализует IEnumerator?
Здесь — низачем, разве что для наглядности.

Цитата Сообщение от loyalist28 Посмотреть сообщение
Зачем сделано отдельное пользовательское определение GetEnumerator()?
Чтобы в форыче вручную не кастить к Person.
Ну и для оптимизации, как сказано выше.
Правда, не в этом примере.

Цитата Сообщение от loyalist28 Посмотреть сообщение
Ведь можно же вместо этого кода сделать так
Можно, но не всегда полезно.
1
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
31.08.2020, 13:59  [ТС]
Цитата Сообщение от kolorotur Посмотреть сообщение
и не плодить мусор в куче при каждом переборе.
А разве после перебора объект перечислитель не уничтожается?

Добавлено через 1 минуту
Цитата Сообщение от kolorotur Посмотреть сообщение
Чтобы в форыче вручную не кастить к Person.
Не особо понятно, в каком случае нам пришлось бы в ручную кастить к Person. Разъясните, пожалуйста, поподробнее.

Добавлено через 44 секунды
Цитата Сообщение от kolorotur Посмотреть сообщение
Чтобы можно было использовать свойство Current, возвращающее Person вместо object, и избежать каста.
Хитро Но это, как я понял, если мы сами вызывать будем где то в коде, верно? Циклы foreach и for используют переменную IEnumerator внутри себя для работы, так что использоваться будут явные реализации. Или я ошибаюсь?
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,234
31.08.2020, 14:03
Цитата Сообщение от loyalist28 Посмотреть сообщение
Посмотрите внимательнее код.
По ссылке не ходил, пардон.

Цитата Сообщение от loyalist28 Посмотреть сообщение
А разве после перебора объект перечислитель не уничтожается?
IEnumerator.Current возвращает object. Я так понял, в случае перечисления структур - каждый элемент-структура будет упакована - а это нам ни к чему.
1
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
31.08.2020, 14:15  [ТС]
Цитата Сообщение от IamRain Посмотреть сообщение
IEnumerator.Current возвращает object. Я так понял, в случае перечисления структур - каждый элемент-структура будет упакована - а это нам ни к чему.
Я просто не совсем знаю как внутри работают встроенные циклы. Но предполагал, что внутри они как раз таки оперируют переменной IEnumerator, тогда будет вызываться как раз таки явная реализация Current, возвращающая object. Мое предположение. Можете, пожалуйста, разъяснить этот момент?
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,234
31.08.2020, 14:23
Явная реализация используется когда тип объявляенной переменной - это тип интерфейса
То есть если взять пример с MSDN:
C#
1
2
3
4
People peopleList = new People(peopleArray);
IEnumerable ienumerable = (IEnumerable) peopleList;
     foreach (Person p in ienumerable)
        Console.WriteLine(p.firstName + " " + p.lastName);
1
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
31.08.2020, 14:23
Лучший ответ Сообщение было отмечено loyalist28 как решение

Решение

Цитата Сообщение от loyalist28 Посмотреть сообщение
А разве после перебора объект перечислитель не уничтожается?
Любой объект уничтожается рано или поздно.
Вопрос в том, насколько рано и насколько поздно: сразу же по завершении работы метода (в основном) или "когда-нибудь потом".

Цитата Сообщение от loyalist28 Посмотреть сообщение
Не особо понятно, в каком случае нам пришлось бы в ручную кастить к Person.
C#
1
2
3
4
5
IEnumerable people = peopleList;
foreach (var person in people)
{
   Console.WriteLine(p.firstName + " " + p.lastName); // Ошибка компиляции: у object нет свойств firstName и lastName, нужно кастить.
}
Цитата Сообщение от loyalist28 Посмотреть сообщение
если мы сами вызывать будем где то в коде, верно?
Не обязательно.

Цитата Сообщение от loyalist28 Посмотреть сообщение
Циклы foreach и for используют переменную IEnumerator внутри себя для работы, так что использоваться будут явные реализации. Или я ошибаюсь?
Нет, явные реализации будут использоваться только если у типа нет предопределенных методов/свойств, необходимых для работы foreach.

Грубо говоря, для работы foreach не нужно реализовывать IEnumerable/IEnumerator — достаточно чтобы тип, используемый в форыче, имел метод GetEnumerator, не принимающий аргументов и возвращающий тип, у которого есть свойство Current и метод MoveNext, не принимающий аргументов и возвращающий bool.
Если такие методы недоступны, тогда уже проверяются явные реализации интерфейсов.
1
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,234
31.08.2020, 14:40
Цитата Сообщение от kolorotur Посмотреть сообщение
Чтобы в форыче вручную не кастить к Person.
Некастить - это все-таки к generic реализации, а тут кастить все равно придется, не смотря на публичный GetEnumerator и публичный Current типа Person у кастомного enumerator-а.

Цитата Сообщение от loyalist28 Посмотреть сообщение
Зачем сделано отдельное пользовательское определение GetEnumerator()
Обратите внимание, что PeopleEnum реализован аналогично - явная реализация + публичный одноименный метод.

Скорее всего, это просто для наглядности. Вот, мол, есть текущий объект в кастомном Enumerator-е и наша явная реализация его использует.
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
31.08.2020, 14:56
Цитата Сообщение от IamRain Посмотреть сообщение
а тут кастить все равно придется, не смотря на публичный GetEnumerator и публичный Current типа Person у кастомного enumerator-а.
Не, не придется.
Если публичное свойство Current возвращает нужный тип, то каст не нужен.
Естественно, если обращение производится не через переменную интерфейсного типа, но для этого и имеется GetEnumerator, возвращающий конкретный тип.
2
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,234
31.08.2020, 15:06
kolorotur, эм, не пойму, берем пример из MSDN, убираем каст к Person в foreach, и это не компилируется.
Type inference не происходит, если я правильно понял.
C#
1
2
3
4
5
6
7
8
9
10
11
Person[] peopleArray = new Person[3]
        {
            new Person("John", "Smith"),
            new Person("Jim", "Johnson"),
            new Person("Sue", "Rabon"),
        };
 
        People peopleList = new People(peopleArray);
         // используется публичный GetEnumerator, не?
        foreach (var p in peopleList)
            Console.WriteLine(p.firstName + " " + p.lastName);
Добавлено через 2 минуты
То есть IEnumerable как был со своими недостатками, так и остался, лучше мы не сделали. Отдельные члены нужных типов в реализациях интерфейсов - просто для наглядности.
0
Эксперт .NET
 Аватар для kolorotur
17823 / 12973 / 3382
Регистрация: 17.09.2011
Сообщений: 21,261
31.08.2020, 15:11
Цитата Сообщение от IamRain Посмотреть сообщение
берем пример из MSDN, убираем каст к Person в foreach, и это не компилируется.
Да все норм компилируется:


Может вы что-то скопировали не так?
1
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,234
31.08.2020, 15:13
А, все, отбой, не заметил ,что кастомный GetEnumerator - приватный, сделал публичным - все зафурычило.
0
0 / 0 / 0
Регистрация: 24.04.2020
Сообщений: 18
31.08.2020, 15:32  [ТС]
Цитата Сообщение от IamRain Посмотреть сообщение
People peopleList = new People(peopleArray);
IEnumerable ienumerable = (IEnumerable) peopleList;
     foreach (Person p in ienumerable)
        Console.WriteLine(p.firstName + " " + p.lastName);
А вот еще вопрос. В этом вот примере используется явная реализация, т.е. foreach вызывает IEnumerable.GetEnumerator(), который возвращает экземпляр типа IEnumerator, обращается к явной реализации свойства Current и оно уже возвращает object. Получается где то во в внутренних механизмах foreach происходит явный каст к Person: Person p = (Person)object obj? Так? Ну чтобы мы могли в foreach использовать объект как Person, а не как возвращенный object.

Добавлено через 5 минут
Цитата Сообщение от kolorotur Посмотреть сообщение
Грубо говоря, для работы foreach не нужно реализовывать IEnumerable/IEnumerator — достаточно чтобы тип, используемый в форыче, имел метод GetEnumerator, не принимающий аргументов и возвращающий тип, у которого есть свойство Current и метод MoveNext, не принимающий аргументов и возвращающий bool.
Если такие методы недоступны, тогда уже проверяются явные реализации интерфейсов.
Интересно, как это реализовано внутри? В переменную какого типа попадает объект-перечислитель? Явно же не в IEnumerator. Тогда бы всегда явные реализации вызывались и эта хитрая техника, которую вы описали, не работала бы. Я предполагаю как то на обобщениях это завязано или на рефлексии (хотя я с рефлексией еще совсем поверхностно знаком).
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,234
31.08.2020, 15:34
Цитата Сообщение от loyalist28 Посмотреть сообщение
В этом вот примере используется явная реализация
Нет, конкретно в этом примере используется так называется утиная типизация. То есть вызывается первый доступный GetEnumerator, который возвращает реализацию IEnumerator. В IEnumerator-е при переборе используется свойство ближайшее доступное свойство Current - а это уже строгий тип Person. Поэтому каст не нужен.
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,234
31.08.2020, 15:36
Это из примера:
Миниатюры
Непонятный код из примера с сайта MSDN. Реализация IEnumerable  
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,234
31.08.2020, 15:39
А если привести к интерфейсу IEnumerable, то перечисление будет происходить уже в типах интерфейса IEnumerable:
Миниатюры
Непонятный код из примера с сайта MSDN. Реализация IEnumerable  
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
31.08.2020, 15:39
Помогаю со студенческими работами здесь

Непонятный код со страницы сайта вымогателя
Уважаемые форумчане, боюсь что не по адресу, но у меня вопрос. История - нарвался на сайт-вымогатель. Сохранил страницу для дальнейшего...

Реализация IEnumerable
Хочу научиться использовать класс Queue&lt;T&gt; на примере данной структуры: private struct ReceivedMessage { ...

Реализация IEnumerable<>
Всем привет, не понимаю как реализовать интерфейс IEnumerable&lt;KeyValuePair&lt;K, V&gt;&gt; в данном коде: public class...

Реализация IEnumerable, IEnumerator
Ребят подскажите,почему в этом коде ошибку выдаёт?Я через массив прогоняю ссылочные типы.Чтобы это сделать надо реализовать интерфейс...

Реализация интерфейса IEnumerable<T>
Зачем при реализации интерфейса IEnumerable&lt;T&gt; кроме public IEnumerator&lt;T&gt; GetEnumerator() { return...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Отображение реквизитов в документе по условию и контроль их заполнения
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "ПланированиеСпецтехники", разработанного в конфигурации КА2. Данный документ берёт данные из другого нетипового документа. . .
Фото всей Земли с борта корабля Orion миссии Artemis II
kumehtar 04.04.2026
Это первое подобное фото сделанное человеком за 50 лет. Снимок называют новым вариантом легендарной фотографии «The Blue Marble» 1972 года, сделанной с борта корабля «Аполлон-17». Новое фото. . .
Вывод диалогового окна перед закрытием, если документ не проведён
Maks 04.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать программный контроль на предмет проведения документа. . .
Программный контроль заполнения реквизита табличной части документа
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать контроль заполнения реквизита "ПричинаСписания". . .
wmic не является внутренней или внешней командой
Maks 02.04.2026
Решение: DISM / Online / Add-Capability / CapabilityName:WMIC~~~~ Отсюда: https:/ / winitpro. ru/ index. php/ 2025/ 02/ 14/ komanda-wmic-ne-naydena/
Программная установка даты и запрет ее изменения
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: при создании документов установить период списания автоматически. . .
Вывод данных в справочнике через динамический список
Maks 01.04.2026
Реализация из решения ниже выполнена на примере нетипового справочника "Спецтехника" разработанного в конфигурации КА2. Задача: вывести данные из ТЧ нетипового документа. . .
Программное заполнения текстового поля в реквизите формы документа
Maks 01.04.2026
Алгоритм из решения ниже реализован на нетиповом документе "ВыдачаОборудованияНаСпецтехнику" разработанного в конфигурации КА2, в дополнении к предыдущему решению. На форме документа создается. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru