Почему массивы C # используют ссылочный тип для перечисления, а List <T>использует изменяемую структуру? - PullRequest
23 голосов
/ 08 февраля 2012

Из того, что я прочитал, было принято решение о проектировании некоторых перечисляемых типов коллекций в качестве изменяемых структур вместо ссылочных типов по соображениям производительности.List.Enumerator является наиболее известным.

Я исследовал какой-то старый код, который использовал массивы, и был удивлен, обнаружив, что массивы C # возвращают тип SZGenericArrayEnumerator в качестве их универсального типа перечислителя, который является ссылочным типом.

Интересно, кто-нибудь знает, почему универсальный итератор Array был реализован как ссылочный тип, когда во многих других критических по производительности коллекциях вместо этого использовались изменяемые структуры.

Ответы [ 2 ]

36 голосов
/ 08 февраля 2012

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

Хороший вопрос.

Во-первых, вы правы. Хотя в общем случае изменяемые типы значений являются неприятным запахом кода, в данном случае они оправданы:

  • Мутация почти полностью скрыта от пользователя.
  • Маловероятно, что кто-либо будет использовать счетчик в замешательстве.
  • Использование изменяемого типа значения действительно решает проблему реалистичной производительности в чрезвычайно распространенном сценарии.

Мне интересно, знает ли кто-нибудь, почему универсальный итератор Array был реализован как ссылочный тип, когда во многих других критических по производительности коллекциях вместо этого использовались изменяемые структуры.

Потому что, если вы относитесь к категории людей, которых беспокоит производительность перечисления массива, тогда почему вы вообще используете перечислитель? Это массив для Ради всего святого; просто напишите цикл for, который перебирает свои индикаторы, как обычный человек, и никогда не выделяет перечислитель. (Или цикл foreach; компилятор C # перезапишет цикл foreach в эквивалентный цикл for, если он знает, что коллекция циклов является массивом.)

Единственная причина, по которой вы сначала получите перечислитель из массива, это если вы передаете его методу, который принимает IEnumerator<T>, в этом случае , если перечислитель является структурой, тогда вы в любом случае будешь заниматься боксом . Зачем брать на себя создание типа значения, а затем его упаковку? Просто сделайте его ссылочным типом.

2 голосов
/ 08 февраля 2012

Массивы получают специальную обработку в компиляторе C #.Когда вы используете foreach для них, компилятор переводит его в цикл for.Таким образом, при использовании struct перечислителей нет никакого выигрыша в производительности. С другой стороны,

List<T> - это простой класс без какой-либо специальной обработки, поэтому использование struct приводит к повышению производительности.

...