Выбрать главу

  // конец метода Calc::'add' 

Преимущества языка CIL

В этот момент вас может интересовать, какую выгоду приносит компиляция исходного кода в CIL, а не напрямую в специфичный набор инструкций. Одним из преимуществ является языковая интеграция. Как вы уже видели, все компиляторы .NET Core выпускают практически идентичные инструкции CIL. Следовательно, все языки способны взаимодействовать в рамках четко определенной "двоичной арены".

Более того, учитывая независимость от платформы языка CIL, сама инфраструктура .NET Core не зависит от платформы и обеспечивает те же самые преимущества, к которым так привыкли разработчики на Java (например, единую кодовую базу, функционирующую в средах многочисленных операционных систем). В действительности для языка C# предусмотрен международный стандарт. До выхода .NET Core существовало множество реализаций .NET для платформ, отличающихся от Windows, таких как Mono. Они по-прежнему доступны, хотя благодаря межплатформенной природе .NET Core потребность в них значительно снизилась.

Компиляция кода CIL в инструкции, специфичные для платформы

Поскольку сборки содержат инструкции CIL, а не инструкции, специфичные для платформы, перед применением код CIL должен компилироваться на лету. Компонентом, который транслирует код CIL в содержательные инструкции центрального процессора (ЦП), является оперативный (JIT) компилятор (иногда называемый jitter). Для каждого целевого ЦП исполняющая среда .NET Core задействует JIT-компилятор, который оптимизирован под лежащую в основе платформу.

Скажем, если строится приложение .NET Core, предназначенное для развертывания на карманном устройстве (наподобие смартфона с iOS или Android), то соответствующий JIT-компилятор будет оснащен возможностями запуска в среде с ограниченным объемом памяти. С другой стороны, если сборка развертывается на внутреннем сервере компании (где память редко оказывается проблемой), тогда JIT-компилятор будет оптимизирован для функционирования в среде с большим объемом памяти. Таким образом, разработчики могут писать единственный блок кода, который способен эффективно транслироваться JIT-компилятором и выполняться на машинах с разной архитектурой.

Вдобавок при трансляции инструкций CIL в соответствующий машинный код JIT-компилятор будет кешировать результаты в памяти в манере, подходящей для целевой ОС. В таком случае, если производится вызов метода по имени PrintDocument(), то инструкции CIL компилируются в специфичные для платформы инструкции при первом вызове и остаются в памяти для более позднего использования. Благодаря этому при вызове метода PrintDocument() в следующий раз повторная компиляция инструкций CIL не понадобится.

Предварительная компиляция кода CIL в инструкции, специфичные для платформы

В .NET Core имеется утилита под названием crossgen.exe, которую вы можете использовать для предварительной компиляции JIT своего кода. К счастью, в .NET Core 3.0 возможность производить "готовые к запуску" сборки встроена в инфраструктуру. Более подробно об этом речь пойдет позже в книге.

Роль метаданных типов .NET Core

В дополнение к инструкциям CIL сборка .NET Core содержит полные и точные метаданные, которые описывают каждый определенный в двоичном модуле тип (например, класс, структуру, перечисление), а также члены каждого типа (скажем, свойства, методы, события). К счастью, за выпуск актуальных метаданных типов всегда отвечает компилятор, а не программист. Из-за того, что метаданные .NET Core настолько основательны, сборки являются целиком самоописательными сущностями.

Чтобы проиллюстрировать формат метаданных типов .NET Core, давайте взглянем на метаданные, которые были сгенерированы для исследуемого ранее метода Add() класса Calc, написанного на C# (метаданные для версии Visual Basic метода Add() похожи, так что будет исследоваться только версия С#):

TypeDef #2 (02000003)

--------------------------------------------------------

  TypDefName: CalculatorExamples.Calc (02000003)

  Flags     :[NotPublic] [AutoLayout] [Class] [AnsiClass]

[BeforeFieldlnit] (00100000)

Роль манифеста сборки

Последний, но не менее важный момент: вспомните, что сборка .NET Core содержит также и метаданные, которые описывают ее саму (формально называемые манифестом). Помимо прочего манифест документирует все внешние сборки, которые требуются текущей сборке для ее корректного функционирования, номер версии сборки, информацию об авторских правах и т.д. Подобно метаданным типов за генерацию манифеста сборки всегда отвечает компилятор. Ниже представлены некоторые существенные детали манифеста, сгенерированного при компиляции показанного ранее в главе файла кода Calc.cs (ради краткости некоторые строки не показаны):