Подтвердить что ты не робот

Почему массивы С# используют ссылочный тип для Enumeration, но List <T> использует измененную структуру?

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

Ответ 2

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

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