Почему JsonElement.ArrayEnumerator реализует IEnumerable?

Я понял, что могу получить перечислитель из ArrayEnumerator, который сам по себе уже является IEnumerator. Ни List, ни Array этого не имеет. Мне любопытно, в чем причина реализации IEnumerable на IEnumerator ? Я не использую вызовы GetEnumerator(), я знаю foreach, мне интересно, какие требования или варианты использования перечислителя приведут к такому выбору конструкции. Это не считалось необходимым для наиболее часто используемых массивов и списков. типы, так почему же он предназначен для типа ArrayEnumerator? Вот код linqpad, который демонстрирует то, о чем я говорю:

var myArr = new int[] {1,2,3};
var enumerator1 = myArr.GetEnumerator(); //no more .GetEnumerator()

var myLst = new List<int>();
myLst.Add(1); myLst.Add(2);
myLst.GetEnumerator(); //no more .GetEnumerator()

var jsonDoc = System.Text.Json.JsonDocument.Parse("[\"a\",\"b\",\"c\"]");
System.Text.Json.JsonElement.ArrayEnumerator arrEnum = jsonDoc.RootElement.EnumerateArray();
System.Text.Json.JsonElement.ArrayEnumerator arrEnum1 = arrEnum.GetEnumerator();

foreach (var el in arrEnum){
    el.Dump("0");
}

foreach(var el in arrEnum1){
    el.Dump("1");
}

Это не класс Enumerator общего назначения, хотя ничто не мешает классу реализовать как IEnumerator<T>, так и IEnumerable<T>, если это служит определенной цели. JsonElement.ArrayEnumerator не является универсальным перечислителем, он является частью System.Text.Json и реализует только IEnumerator<JsonElement> и IEnumerable<JsonElement>. Сравнивать его со списком или массивом — значит сравнивать яблоки с апельсинами. Вы можете проверить актуальный исходный код , чтобы увидеть, как он реализован

Panagiotis Kanavos 19.03.2024 09:23

Если бы у него не было метода GetEnumerator, вы бы не смогли использовать для него foreach. Это звучит бесполезно... Хотели бы вы вместо этого JsonElement иметь метод GetEnumerator? Это не выглядит логично, ИМХО.

Sweeper 19.03.2024 09:26

STJ создан для повышения производительности, и использование одного класса вместо нескольких экономит одно размещение объектов на каждый сложный элемент. Если вы проверите источник, вы увидите, что ArrayEnumerator использует индекс в списке элементов родительского элемента. Это может сделать только вложенный класс. Конструктор является внутренним, поэтому только классы STJ могут использовать этот перечислитель.

Panagiotis Kanavos 19.03.2024 09:27

Почему вы вообще используете перечислитель напрямую? Почему бы не использовать foreach? Редко можно увидеть, чтобы какие-либо приложения использовали перечислители напрямую. Вы видите это только в специализированных библиотеках, таких как MoreLinq, System.Text.Json, System.Linq.Async или самом LINQ, которые реализуют пользовательские операции перечисления или имеют особые требования к производительности.

Panagiotis Kanavos 19.03.2024 09:31

@PanagiotisKanavos приведенный выше код работает нормально, поэтому оба перечислителя можно использовать. Мне любопытен вариант использования перечислителя в качестве перечислителя. Т.е. какое требование привело к созданию этого дизайна?

GrumpyRodriguez 19.03.2024 11:13

Я ответил на это и даже дал ссылку на исходный файл. Нет причин использовать отдельные классы, но за это может потребоваться плата. STJ был создан для устранения выделения памяти, поэтому выделение одного объекта вместо двух всегда является преимуществом, особенно в классе, который вызывается в приложении тысячи раз. Для получения более подробной информации вам придется проверить репозиторий GitHub. STJ имеет открытый исходный код, а вопросы разработки и проектные решения документированы на Github.

Panagiotis Kanavos 19.03.2024 11:21

@PanagiotisKanavos Хорошо, думаю, теперь я это вижу. Вы имеете в виду, что это следствие того, что IEnumerable и IEnumerator реализованы в одном и том же типе для уменьшения давления GC? то есть нет варианта использования, но это результат реализации того же интерфейса в конкретном классе и возврата самого класса, что приводит к тому, что вызывающая сторона получает исходный Ienumerable. Что объясняет его. Хотите написать ответ, чтобы я мог его принять? Рад написать, но ответили вы, а не я.

GrumpyRodriguez 19.03.2024 11:31
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
7
89
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это основано на комментарии @panagiotis-kanavos. Вкратце: нет дизайнерского решения использовать IEnumerator<T> в качестве IEnumerable<T>. Причина, по которой ArrayEnumeratorGetEnumerator() возвращает как ожидаемый IEnumerator<T>, так и IEnumerable<T>, заключается в том, что он возвращает конкретно самого себя, то есть тип ArrayEnumerator, который поддерживает оба интерфейса. Этот тип реализует оба интерфейса, чтобы уменьшить количество выделений.

Я лично подозреваю, что причина возврата самого типа, а не интерфейса, заключается в том, чтобы указать тот факт, что тип реализует оба интерфейса, но я не уверен насчет этого. Тем не менее, ответ на мой вопрос заключается в том, что это следствие решения, принятого по соображениям производительности, а не связанного с повторным использованием IEnumerator<T> в качестве IEnumerable<T>.

Другие вопросы по теме