Я рекомендую прочитать Обобщения в 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, иначе вы получите ошибку времени компиляции. Или, если вы делаете это с отражением, вы получите исключение.
Брюс Экель: Вы сказали, что компилятор и среда выполнения.
Андерс Хейлсберг: Компилятор проверяет это, но вы также можете делать это во время выполнения с отражением, а затем система проверяет это. Как я уже говорил ранее, все, что вы можете делать во время компиляции, вы также можете делать во время выполнения с отражением.