Как генерики генерируются компилятором JIT? - PullRequest
27 голосов
/ 17 марта 2011

Я знаю, что дженерики компилируются JIT (как и все остальное) в отличие от шаблонов, которые генерируются при компиляции кода. Дело в том, что новые универсальные типы могут быть созданы во время выполнения с помощью отражения.
Что, конечно, может повлиять на ограничения универсального. Который уже прошел семантический парсер.

Может кто-нибудь объяснить, как это обрабатывается? И что именно происходит?
(Как генерация кода, так и семантическая проверка)

Ответы [ 2 ]

36 голосов
/ 17 марта 2011

Я рекомендую прочитать Обобщения в C #, Java и C ++: разговор с Андерсом Хейлсбергом .

Qn 1. Как генерики компилируются компилятором JIT?

Из интервью:

Андерс Хейлсберг: [...] В CLR [Common Language Runtime], когда вы компилируете List, или любой другой универсальный тип, онкомпилируется до IL [Intermediate Language] и метаданных, как любой обычный тип.IL и метаданные содержат дополнительную информацию, которая, конечно, знает, что есть параметр типа, но в принципе универсальный тип компилируется так же, как компилируется любой другой тип.Во время выполнения, когда ваше приложение впервые ссылается на List, система проверяет, не запросил ли кто-нибудь уже List<int>.Если нет, он передает в JIT IL и метаданные для List<T> и аргумент типа int.JITer, в процессе JITing IL, также заменяет параметр типа.

[...]

Теперь то, что мы затем делаем, это для всех экземпляров типов, которые являются типами значений.например, List<int>, List<long>, List<double>, List<float> - мы создаем уникальную копию исполняемого собственного кода.Так что List<int> получает свой собственный код.List<long> получает свой собственный код.List<float> получает свой собственный код.Для всех ссылочных типов мы разделяем код, потому что они представительно идентичны.Это всего лишь указатели.


Qn 2. Дело в том, что новые универсальные типы можно создавать во время выполнения с помощью отражения.Что, конечно, может повлиять на ограничения универсального.Который уже прошел семантический парсер.Может кто-нибудь объяснить, как это обрабатывается?

По сути, IL сохраняет высокоуровневое представление универсальных типов, которое позволяет CLR проверять ограничения для «динамически создаваемых» универсальных типов во время выполнения, каккомпилятор C # может делать «статически сконструированные» типы в исходном коде C # во время компиляции.

Вот еще один фрагмент (выделено мной):

Андерс Хейлсберг: [...] С ограничением вы можете поднять эту динамическую проверку из своего кода и сделать ее проверяемой во время компиляции или загрузки. Когда вы говорите, что K должен реализовать IComparable, происходит пара вещей.При любом значении типа K вы теперь можете напрямую обращаться к методам интерфейса без приведения, потому что семантически в программе гарантируется, что он будет реализовывать этот интерфейс.Всякий раз, когда вы пытаетесь создать экземпляр этого типа, компилятор проверяет, что любой тип, который вы задаете в качестве аргумента K, реализует IComparable, иначе вы получите ошибку времени компиляции. Или, если вы делаете это с отражением, вы получите исключение.

Брюс Экель: Вы сказали, что компилятор и среда выполнения.

Андерс Хейлсберг: Компилятор проверяет это, но вы также можете делать это во время выполнения с отражением, а затем система проверяет это. Как я уже говорил ранее, все, что вы можете делать во время компиляции, вы также можете делать во время выполнения с отражением.

2 голосов
/ 17 марта 2011

Все эталонные типы ссылок становятся одинаковыми типами; Обобщения типа значения создаются отдельно .

Это потому, что все ссылочные типы на самом деле являются просто Object ссылками (4 или 8 байтов), тогда как типы значений различны и не могут обрабатываться одним фрагментом кода из-за различий в расположении стека и т. Д. несколько копий универсального типа со значениями типов значительно увеличат использование памяти, тогда как создание нескольких копий со ссылочными типами не будет.

...