Независимо от того, какой язык .NET Core выбран для программирования, важно понимать, что хотя двоичные модули .NET Core имеют такое же файловое расширение, как и неуправляемые двоичные компоненты Windows (*.dll
), внутренне они устроены совершенно по-другому. В частности, двоичные модули .NET Core содержат не специфические, а независимые от платформы инструкции на промежуточном языке (Intermediate Language — IL) и метаданные типов.
На заметку! Язык IL также известен как промежуточный язык Microsoft (Microsoft Intermediate Language — MSIL) или общий промежуточный язык (Common Intermediate Language — CIL). Таким образом, при чтении литературы по .NET/.NET Core не забывайте о том, что IL, MSIL и CIL описывают в точности одну и ту же концепцию. В настоящей книге при ссылке на этот низкоуровневый набор инструкций будет применяться аббревиатура CIL.
Когда файл *.dll
был создан с использованием компилятора .NET Core, результирующий большой двоичный объект называется сборкой. Все многочисленные детали, касающиеся сборок .NET Core, подробно рассматриваются в главе 16. Тем не менее, для упрощения текущего обсуждения вы должны усвоить четыре основных свойства нового файлового формата.
Во-первых, в отличие от сборок .NET Framework, которые могут быть файлами *.dll
или *.ехе
, проекты .NET Core всегда компилируются в файл с расширением .dll
, даже если проект является исполняемым модулем. Исполняемые сборки .NET Core выполняются с помощью команды dotnet<имя_сборки>.dll
. Нововведение .NET Core 3.0 (и последующих версий) заключается в том, что команда dotnet.ехе
копирует файл в каталог сборки и переименовывает его на <имя_сборки>.ехе
. Запуск этой команды автоматически выполняет эквивалент dotnet<имя_сборки>.ехе
. Файл *.ехе
с именем вашего проекта фактически не относится к коду проекта; он является удобным сокращением для запуска вашего приложения.
Нововведением .NET 5 стало то, что ваше приложение может быть сведено до единственного файла, который запускается напрямую. Хотя такой единственный файл выглядит и действует подобно собственному исполняемому модулю в стиле C++, его преимущество заключается в пакетировании. Он содержит все файлы, необходимые для выполнения вашего приложения и потенциально даже саму исполняющую среду .NET 5! Но помните о том, что ваш код по-прежнему выполняется в управляемом контейнере, как если бы он был опубликован в виде множества файлов.
Во-вторых, сборка содержит код CIL, который концептуально похож на байт-код Java тем, что не компилируется в специфичные для платформы инструкции до тех пор, пока это не станет абсолютно необходимым. Обычно "абсолютная необходимость" наступает тогда, когда на блок инструкций CIL (такой как реализация метода) производится ссылка с целью его применения исполняющей средой .NEIT Core.
В-третьих, сборки также содержат метаданные, которые детально описывают характеристики каждого "типа" внутри двоичного модуля. Например, если имеется класс по имени SportsCar
, то метаданные типа представляют такие детали, как базовый класс SportsCar
, указывают реализуемые SportsCar
интерфейсы (если есть) и дают полные описания всех членов, поддерживаемых типом SportsCar
. Метаданные .NET Core всегда присутствуют внутри сборки и автоматически генерируются компилятором языка.
Наконец, в-четвертых, помимо инструкций CIL и метаданных типов сами сборки также описываются с помощью метаданных, которые официально называются манифестом. Манифест содержит информацию о текущей версии сборки, сведения о культуре (используемые для локализации строковых и графических ресурсов) и список ссылок на все внешние сборки, которые требуются для надлежащего функционирования. Разнообразные инструменты, которые можно применять для исследования типов, метаданных и манифестов сборок, рассматриваются в нескольких последующих главах.
Роль языка CIL
Теперь давайте займемся детальными исследованиями кода CIL, метаданных типов и манифеста сборки. Язык CIL находится выше любого набора инструкций, специфичных для конкретной платформы. Например, приведенный далее код C# моделирует простой калькулятор. Не углубляясь пока в подробности синтаксиса, обратите внимание на формат метода Add()
в классе Calc
.
// Calc.cs
using System;
namespace CalculatorExamples
{
// Этот класс содержит точку входа приложения.
class Program
{