Реализация перечислителя: использовать структуру или класс? - PullRequest
11 голосов
/ 21 декабря 2008

Я заметил, что List<T> определяет свой перечислитель как struct, тогда как ArrayList определяет свой перечислитель как class. Какая разница? Если я хочу написать перечислитель для моего класса, какой из них предпочтительнее?

РЕДАКТИРОВАТЬ: Мои требования не могут быть выполнены с помощью yield, поэтому я внедряю свой собственный перечислитель. Тем не менее, мне интересно, будет ли лучше следовать строчкам List<T> и реализовывать это как структуру.

Ответы [ 8 ]

9 голосов
/ 21 декабря 2008

Как и другие, я бы выбрал класс. Изменчивые структуры противны. (И, как предлагает Джаред, я бы использовал блок итератора. Ручное кодирование перечислителя - дело сложное.)

См. в этой теме , где пример перечислителя списка является изменяемой структурой, вызывающей проблемы ...

9 голосов
/ 21 декабря 2008

Самый простой способ написать перечислитель в C # - с помощью шаблона yield return. Например.

public IEnumerator<int> Example() {
  yield return 1;
  yield return 2;
}

Этот шаблон будет генерировать весь код перечислителя под капотом. Это берет решение из ваших рук.

7 голосов
/ 08 ноября 2010

Список причин использует перечислитель структуры для предотвращения генерации мусора в операторах foreach. Это довольно веская причина, особенно если вы программируете для Compact Framework, потому что CF не имеет генеративного GC, а CF обычно используется на низкопроизводительном оборудовании, где это может быстро привести к проблемам с производительностью.

Кроме того, я не думаю, что изменяемые структуры являются источником проблем в примерах, опубликованных некоторыми, но программисты, которые не имеют хорошего понимания того, как работают типы значений.

5 голосов
/ 21 декабря 2008

Перечислитель по своей сути является изменяющейся структурой, поскольку ему необходимо обновить внутреннее состояние, чтобы перейти к следующему значению в исходной коллекции.

По моему мнению, структуры должны быть неизменяемыми, поэтому я бы использовал класс.

5 голосов
/ 21 декабря 2008

Напишите, используя yield return.

Относительно того, почему в противном случае вы можете выбрать между class или struct, если вы сделаете его struct, то оно будет упаковано, как только будет возвращено в качестве интерфейса, поэтому превращение его в struct просто вызывает дополнительное копирование. Не вижу смысла в этом!

2 голосов
/ 03 марта 2011

Любая реализация IEnumerable<T> должна возвращать класс. Из соображений производительности может быть полезно иметь метод GetEnumerator, который возвращает структуру, которая предоставляет методы, необходимые для перечисления, но не реализует IEnumerator<T>; этот метод должен отличаться от IEnumerable<T>.GetEnumerator, который затем должен быть реализован явно.

Использование этого подхода позволит повысить производительность, когда класс перечисляется с использованием цикла foreach или цикла «Для каждого» в C # или vb.net или в любом контексте, в котором код, выполняющий перечисление, будет знать, что перечислитель является структурой , но избегайте ловушек, которые иначе могли бы возникнуть, когда перечислитель упакован и передан по значению.

1 голос
/ 20 мая 2010

Есть пара блогов сообщений , которые освещают именно эту проблему. По сути, структуры перечислителей - действительно очень плохая идея ...

1 голос
/ 21 декабря 2008

Чтобы расширить @Earwicker: вам лучше всего не писать тип перечислителя и вместо этого использовать yield return, чтобы компилятор написал его для вас. Это потому, что есть ряд важных тонкостей, которые вы можете пропустить, если сделаете это сами.

См. Вопрос SO " Для чего используется ключевое слово yield в C #? ", чтобы узнать больше о том, как его использовать.

Также Раймонд Чен имеет ряд сообщений в блоге (« Реализация итераторов в C # и ее последствия »: parts 1 , 2 , 3 и 4 ), которые показывают, как правильно реализовать итератор без yield return, который показывает, насколько он сложен, и почему вы должны просто использовать yield return.

...