Здравствуйте , не могу понять один листинг вот отсюда :
msdn.microsoft.com/ru-ru/library/1t2267t6.aspx
Помимо того , что этот пример у меня не запускается( компилятор намекает , что эти 2 интерфейса шаблонные) ,
я не особо понимаю вот этот к примеру кусок кода ( класса People )
1)Насколько я помню экземпляры интерфейса создавать нельзя ,
потому не понятно как метод IEnumerable.GetEnumerator() может иметь возвращаемый тип интерфейса IEnumerator
2)Метод GetEnumerator() вернет объект класса PeopleEnum , а затем (return (IEnumerator) GetEnumerator())
каким-то образом его приведет опять к интерфейсу IEnumerator , что тоже не совсем понятно .
3)Для чего вообще нужно реализовать эти 2 интерфейса ? По ссылке было написано что-то подобное :
"Они реализуются для поддержки использования синтаксиса foreach (For Each в Visual Basic) с целью перебора элементов коллекции."
ОК , ясно , но следующий код работает с тем же успехом , но никакой реализации этих 2-х интерфейсов нет :
В общем кто-нибудь , кому не сложно, просветите зачем вообще эти 2 интерфейса , а то я
видимо чего-то не догоняю.Заранее спасибо.
msdn.microsoft.com/ru-ru/library/1t2267t6.aspx
Помимо того , что этот пример у меня не запускается( компилятор намекает , что эти 2 интерфейса шаблонные) ,
я не особо понимаю вот этот к примеру кусок кода ( класса People )
1)Насколько я помню экземпляры интерфейса создавать нельзя ,
потому не понятно как метод IEnumerable.GetEnumerator() может иметь возвращаемый тип интерфейса IEnumerator
2)Метод GetEnumerator() вернет объект класса PeopleEnum , а затем (return (IEnumerator) GetEnumerator())
каким-то образом его приведет опять к интерфейсу IEnumerator , что тоже не совсем понятно .
3)Для чего вообще нужно реализовать эти 2 интерфейса ? По ссылке было написано что-то подобное :
"Они реализуются для поддержки использования синтаксиса foreach (For Each в Visual Basic) с целью перебора элементов коллекции."
ОК , ясно , но следующий код работает с тем же успехом , но никакой реализации этих 2-х интерфейсов нет :
В общем кто-нибудь , кому не сложно, просветите зачем вообще эти 2 интерфейса , а то я
видимо чего-то не догоняю.Заранее спасибо.
Он может вернуть экземпляр любого класса, реализующего интерфейс IEnumerator. Сам класс объекта тут не важен. Важно только то, что он соответствует указанному интерфейсу, т.е. имеет нужные методы.
2)Метод GetEnumerator() вернет объект класса PeopleEnum , а затем (return (IEnumerator) GetEnumerator())
каким-то образом его приведет опять к интерфейсу IEnumerator , что тоже не совсем понятно .
Как я понимаю, это для того, чтобы можно было безболезненно делать как
PeopleEnum peopleEnum = people.GetEnumerator();
так и
IEnumerator peopleEnum = people.GetEnumerator();
Хотя, честно говоря не уверен.
3)Для чего вообще нужно реализовать эти 2 интерфейса ?
Судя по всему, IEnumerable и IEnumerator - это интерфейсы, позволяющие реализовать паттерн проектирования "Итератор".
Что это такое и нафига это нужно.
Вот приведённый вами класс People - по сути бесполезен, он нифига не делает, а просто хранит ссылку на массив людей. Поэтому его легко можно заменить просто самим массивом (кстати, все массивы в C# реализуют интерфейс IEnumerable).
Допустим, во второй версии программы мы решили, что этих троих (Джона Смита, Джима Джонсона и Сью Рэйбон) мы уже и так знаем, и нефиг каждый раз нам их выводить на экран. Теперь нам хочется, чтобы люди читались из файла.
Поэтому, для начала добавим классу People функцию, пусть, ReadFromFile(). И, значит, придётся добавить ещё и функцию, которая будет выдавать прочитанных людей - чтобы в цикле выводить их имена и фамилии. Пусть это будет GetPersons(). Вот что получаем:
Всё прекрасно. Ну, точнее всё прекрасно работает. Нехорошо только то, что на GetPresons() мы возвращаем сам внутренний массив класса People. Когда наша софтина разрастётся, и необходимо будет нанимать ещё кодеров, кто-нибудь обязательно забудет, что этот массив - часть внутреннего состояния объекта, и чего-нибудь да накосячит. Можно, конечно сделать что-то типа "return (Persons[]) _people.Clone()", но это а) медленно (Clone() является операцией O(n), где n - длина массива) б) засирает память копией массива - а ведь нам-то всего и надо, что пройтись по людям в цикле - и в) сборщик мусора сработает на клонированный массив? Ну да ладно, пока что забьём на это.
Так. Идём дальше. А дальше - нам захотелось, чтобы в третьей версии программа запрашивала у пользователя, надо ли добавить кого к людям, а под конец - сохраняла этих добавленных обратно в файл. Т.е. надо, чтобы класс People мог динамически добавлять людей в себя, а также сохраняться в файл. Т.е. ему добавляются функции Add() и WriteToFile().
Но дальше мы встаём перед дилеммой. Если _people имеет тип Person[], то для его расширения придётся использовать Array.Resize(), что медленно (Array.Resize() является операцией O(n), где n - новая длина массива). Если же _people сделать списком List, то добавление нового элемента будет куда быстрее. Однако в этом случае придётся либо в GetPersons создавать массив из этого списка (что, опять же, медленно и засирает память), либо переписывать класс Program так, чтобы в циклах использовались не массивы, а списки. Благо, у нас сейчас всего один цикл, и мы решаем пойти по пути улучшения быстродействия - т.е. использовать списки вместо массивов.
Ок. Решились. Пишем:
И опять же - на GetPersons() возвращаем либо внутренний массив, что плохо, либо его копию, что тоже плохо.
Ну да хрен с ним, опять же.А не-не-не, не хрен! Ведь List имеет функцию AsReadOnly()!Клёво! Снова всё переписывать! =) Что мы и делаем, получая версию три.один =)
Вот теперь всё клёво! Да, точно клёво! Кроме того, что...
Кроме того, что рано или поздно появится необходимость поиска людей в People по имени-фамилии. А значит, либо пользоваться полным перебором в списке _people, что медленно, либо делать _people типа, скажем, Dictionary. И переписывать всю программу (версия четыре).
А потом вдруг окажется, что у нас ведь Джон Смит может быть не один, а несколько. А Dictionary не может иметь два одинаковых ключа. И придётся вообще реализовывать свой какой-то тип коллекции с ключами. И снова всё переписывать (версия четыре.один).
А потом ещё что-нибудь всплывёт, и ещё, и ещё - и так, как показывает опыт, может длиться очень долго.
А теперь представьте, что цикл по людям у нас в программе не один, а штук так двадцать. И разбросаны они по пятнадцати модулям из сотни, каждый из которых закреплён за одним из пяти программистов. И каждое изменение механизма хранения людей из People, таким образом, в конце-концов начинает вызывать лютую головную боль у каждого из них.
Что же делать? А решение очень простое. Нам ведь в конечном-то счёте что нужно? Нам вовсе не нужно в основной программе знать, как там именно в классе People хранятся люди. Нам надо просто получить какой-то объект-итератор, который нам помог бы по этим людям пройтись циклом. Нам вполне достаточно, чтобы этот итератор говорил нам, остались ли ещё какие объекты для цикла и выдавал эти объекты один за другим. Всё. Большего нам не надо. Т.е., иными словами, нам нужен экземпляр класса, реализующего интерфейс IEnumerator.
И тогда, переписав:
мы сможем менять механизм хранения людей в классе People так, как нам заблагорассудится, при этом не внося вообще никаких изменений в основную программу.
Также, если нам, например, вдруг понадобится во всех циклах изменить порядок обхода, тогда нам не придётся переписывать сами циклы, мы просто перепишем возвращаемый по GetPersons() итератор.
Да, я тут всё про IEnumerator, да про IEnumerator. А для чего же IEnumerable? А для того, чтобы реализовав этот интерфейс в классе Peope, вообще не париться со всякими разными GetPersons(), а прямо писать:
foreach (Person p in people) /*...*/;
Но это уже синтаксический сахар, по-моему =)
Не по теме
Не за что. Тем более, что я допустил ошибку. foreach ведь принимает не IEnumerator, а IEnumerable - а я в него упорно IEnumerator совал. Так что как я написал работать не будет. Но, надеюсь, основная идея понятна.
сколько,если не секрет , лет вы уже программируете?Просто интересно
"Занимаюсь программированием" в том или ином виде, наверное, уже лет пятнадцать. Т.е. потихоньку учу языки и приёмы программирования, различные технологии и т.п. В качестве хобби. Ну не называть же "программированием" первые опыты написания игрушек на бэйсике под МК-85М в далёком 1995'ом, копания в дельфях в 1999'ом или выполнение домашних заданий по ЦОС'у в универе.
А опыт собственно работы в области программирования совсем скудный - в общей сложности около полутора лет, наверное. И чем больше работаю, тем больше понимаю, что научиться надо ещё многому.