Я понял, что могу получить перечислитель из 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");
}
Если бы у него не было метода GetEnumerator, вы бы не смогли использовать для него foreach. Это звучит бесполезно... Хотели бы вы вместо этого JsonElement иметь метод GetEnumerator? Это не выглядит логично, ИМХО.
STJ создан для повышения производительности, и использование одного класса вместо нескольких экономит одно размещение объектов на каждый сложный элемент. Если вы проверите источник, вы увидите, что ArrayEnumerator использует индекс в списке элементов родительского элемента. Это может сделать только вложенный класс. Конструктор является внутренним, поэтому только классы STJ могут использовать этот перечислитель.
Почему вы вообще используете перечислитель напрямую? Почему бы не использовать foreach? Редко можно увидеть, чтобы какие-либо приложения использовали перечислители напрямую. Вы видите это только в специализированных библиотеках, таких как MoreLinq, System.Text.Json, System.Linq.Async или самом LINQ, которые реализуют пользовательские операции перечисления или имеют особые требования к производительности.
@PanagiotisKanavos приведенный выше код работает нормально, поэтому оба перечислителя можно использовать. Мне любопытен вариант использования перечислителя в качестве перечислителя. Т.е. какое требование привело к созданию этого дизайна?
Я ответил на это и даже дал ссылку на исходный файл. Нет причин использовать отдельные классы, но за это может потребоваться плата. STJ был создан для устранения выделения памяти, поэтому выделение одного объекта вместо двух всегда является преимуществом, особенно в классе, который вызывается в приложении тысячи раз. Для получения более подробной информации вам придется проверить репозиторий GitHub. STJ имеет открытый исходный код, а вопросы разработки и проектные решения документированы на Github.
@PanagiotisKanavos Хорошо, думаю, теперь я это вижу. Вы имеете в виду, что это следствие того, что IEnumerable и IEnumerator реализованы в одном и том же типе для уменьшения давления GC? то есть нет варианта использования, но это результат реализации того же интерфейса в конкретном классе и возврата самого класса, что приводит к тому, что вызывающая сторона получает исходный Ienumerable. Что объясняет его. Хотите написать ответ, чтобы я мог его принять? Рад написать, но ответили вы, а не я.





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