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

Почему компилятор-сгенерированный счетчик для "yield" не является структурой?

компилятор-сгенерированная реализация IEnumerator/IEnumerable для yield методов и геттеров, кажется, является классом и поэтому распределяется по куче, Однако другие типы .NET, такие как List<T>, специально возвращают счетчики struct, чтобы избежать бесполезного распределения памяти. Из краткого обзора сообщения С# In Depth я не вижу причин, почему это не может быть и здесь.

Я что-то пропустил?

4b9b3361

Ответ 1

Сервис правильно ответил на ваш вопрос - вопрос, на который вы ответили самим в комментарии:

Я просто понял, что, поскольку тип возврата является интерфейсом, он все равно будет в коробке, верно?

Right. Ваш следующий вопрос:

не удалось изменить метод, чтобы вернуть явно типизированный счетчик (например, List<T>)?

Итак, ваша идея заключается в том, что пользователь пишет:

IEnumerable<int> Blah() ...

и компилятор фактически генерирует метод, который возвращает BlahEnumerable, который является структурой, реализующей IEnumerable<int>, но с соответствующими методами и свойствами GetEnumerator и т.д., которые позволяют использовать функцию соответствия шаблону foreach бокс.

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

  • Предположим, что метод является виртуальным. Как его можно переопределить? Тип возвращаемого метода виртуального переопределения должен точно соответствовать переопределенному методу. (И аналогично: метод переопределяет другой метод, метод реализует метод интерфейса и т.д.)

  • Предположим, что метод внесен в делегат Func<IEnumerable<int>>. Func<T> ковариантно в T, но ковариация применяется только к аргументам типа ссылочного типа. Код выглядит так, как будто он возвращает IEnumerable<T>, но на самом деле он возвращает тип значения, который не совместим с ковариацией с IEnumerable<T>, только совместимым присваиванием.

  • Предположим, что мы имеем void M<T>(T t) where T : class и называем M(Blah()). Мы ожидаем, что T есть IEnumerable<int>, который передает проверку ограничений, но тип структуры не проходит проверку привязки.

И так далее. Вы быстро попадаете в эпизод из Three Company (мальчик, я встречаюсь здесь), где небольшая ложь заканчивается сложной катастрофой. Все это позволяет сэкономить небольшое количество давления на сбор. Не стоит.

Я отмечаю, что реализация, созданная компилятором, экономит на собирающем давлении одним интересным способом. В первый раз, когда GetEnumerator вызывается в возвращаемом перечисляемом, перечислимое превращается в перечислитель. Второй раз, конечно, состояние отличается, поэтому он выделяет новый объект. Поскольку вероятный сценарий с вероятностью 99,99% состоит в том, что данная последовательность перечисляется ровно один раз, это большая экономия на введении давления.

Ответ 2

Этот класс будет использоваться только через интерфейс. Если бы это была структура, она была бы упакована в 100% случаев, что делает ее менее эффективной, чем использование класса.

Вы не можете вставить его, поскольку по определению невозможно использовать тип во время компиляции, поскольку он не существует, когда вы начинаете компилировать код.

При написании пользовательской реализации IEnumerator вы можете выставить фактический базовый тип перед компиляцией кода, что позволяет использовать его без использования.