Формат сборки .NET
98C# --- Сборки .NET --- Формат сборки .NET
Ознакомившись с некоторыми преимуществами сборок .NET. давайте более детально рассмотрим, как эти сборки устроены внутри. С точки зрения структуры любая сборка .NET (*.dll или *.ехе) включает в себя следующие элементы:
заголовок файла Windows;
заголовок файла CLR;
CIL-код;
метаданные типов;
манифест сборки;
дополнительные встроенные ресурсы.
Хотя первые два элемента представляют собой такие блоки данных, на которые обычно можно не обращать внимания, краткого рассмотрения они все-таки заслуживают. Ниже предлагаются краткие описания всех перечисленных элементов.
Заголовок файла Windows
Заголовок файла Windows указывает на тот факт, что сборка может загружаться и использоваться в операционных системах семейства Windows. Кроме того, этот заголовок идентифицирует тип приложения (консольное, приложение с графическим пользовательским интерфейсом или библиотека кода *.dll). Чтобы просмотреть информацию, содержащуюся в заголовке Windows, необходимо открыть сборку .NET в утилите dumpbin.ехе (в окне командной строки Visual Studio 2010) с указанием флага /headers:
dumpbin /headers Shapes.dll
Ниже показано, как будет выглядеть информация в заголовке Windows сборки Program.exe:
Теперь, важно уяснить, что большей части программистов, работающей с .NET, никогда не придется беспокоиться о формате встраиваемых в сборку .NET данных заголовков. Если только не требуется разрабатывать новый компилятор для одного из языков .NET (где такая информация действительно важна), можно не вникать в тонкие детали данных заголовков. Однако помните, что эта информация используется "за кулисами", когда Windows загружает двоичный образ в память.
Заголовок файла CLR
Заголовок CLR представляет собой блок данных, который должны обязательно поддерживать все сборки .NET (что они и делают, благодаря компилятору C#) для того, чтобы они могли обслуживаться в CLR-среде. Вкратце, в этом заголовке определяются многочисленные флаги, которые позволяют исполняющей среде разобраться в компоновке управляемого файла. Например, эти флаги указывают, где внутри файла находятся метаданные и ресурсы, как выглядит версия исполняющей среды, на которую ориентировалась данная сборка, каково (необязательное) значение открытого ключа, и т.д. Чтобы просмотреть данные заголовка CLR в сборке .NET, необходимо открыть сборку в утилите dumpbin.exe с указанием флага /clrheader:
dumpbin /clrheader Shapes.dll
Опять-таки, разработчики приложений .NET не должны беспокоиться о тонких деталях заголовков CLR. Главное понимать, что такие данные обязательно содержатся в каждой сборке .NET и применяются исполняющей средой .NET при загрузке образа в память. Теперь рассмотрим информацию, которая является гораздо более полезной при решении повседневных программистских задач.
CIL-код, метаданные типов и манифест сборки
В своей основе любая сборка содержит код на языке CIL, который, представляет собой промежуточный язык, не зависящий ни от платформы, ни от процессора. Во время выполнения внутренний CIL-код "на лету" (посредством JIT-компилятора) компилируется в инструкции, соответствующие требованиям конкретной платформы и процессора. Благодаря этому, сборки .NET в действительности могут выполняться в рамках разнообразных архитектур, устройств и операционных систем.
В любой сборке содержатся метаданные, которые полностью описывают формат находящихся внутри нее типов, а также внешних типов, на которые она ссылается. В исполняющей среде .NET эти метаданные используются для выяснения того, в каком месте внутри двоичного файла находятся типы (и их члены), для размещения типов в памяти и для упрощения процесса удаленного вызова методов.
Кроме того, в любой сборке должен обязательно содержаться ассоциируемый с ней манифест (по-другому еще называемый метаданными сборки). В этом манифесте описан каждый входящий в состав сборки модуль, версия сборки, а также любые внешние сборки, на которые ссылается текущая сборка (что отличает сборки от библиотек типов СОМ, в которых не существовало никакой возможности для документирования внешних зависимостей). Как будет показано далее в главе, манифест сборки интенсивно применяется в CLR-среде во время определения места, на которое указывают представляющие внешние сборки ссылки.
Необязательные ресурсы сборки
И, наконец, в любой сборке .NET может также содержаться любое количество вложенных ресурсов, таких как значки приложения, графические файлы, звуковые фрагменты или таблицы строк. На самом деле платформа .NET поддерживает возможность создания даже так называемых подчиненных сборок (satellite assemblies), содержащих только локализованные ресурсы и больше ничего. Эта возможность может быть очень удобной, когда необходимо разделить ресурсы по конкретным культурам (английской, немецкой и т.п.) при построении программного обеспечения, которое предназначено для использования по всему миру.
Однофайловые и многофайловые сборки
С технической точки зрения сборка может состоять из нескольких так называемых модулей (module). Модуль на самом деле представляет собой не более чем просто общий термин, применяемый для обозначения действительного двоичного файла .NET. В большинстве ситуаций, однако, сборка состоит из единственного модуля. В этом случае между (логической) сборкой и лежащим в ее основе (физическим) двоичным файлом существует соответствие типа "один к одному" (откуда и происходит термин "однофайловая сборка").
В однофайловых сборках все необходимые элементы (заголовки Windows и CIL, метаданные типов, манифест и необходимые ресурсы) размещаются внутри единственного пакета *.ехе или *.dll. На рисунке схематично показано устройство однофайловой сборки:
Многофайловая сборка, с другой стороны, состоит из набора модулей .NET, которые развертываются в виде одной логической единицы и снабжаются одним номером версии. Формально один из этих модулей называется главным модулем и содержит манифест сборки (а также все необходимые CIL-инструкции, метаданные, заголовки и дополнительные ресурсы).
В манифесте главного модуля описаны все остальные связанные модули, от которых зависит его работа. По соглашению второстепенным модулям в многофайловой сборке назначается расширение *.netmodule; обязательным требованием для CLR-среды это, однако, не является. Во второстепенных модулях *.netmodule тоже содержится CIL-код и метаданные типов, а также манифест уровня модуля, в котором перечислены внешние сборки, необходимые данному модулю.
Главное преимущество многофайловых сборок состоит в том, что они предоставляют очень эффективный способ для выполнения загрузки содержимого. Например, предположим, что есть машина, которая ссылается на удаленную многофайловую сборку, состоящую из трех модулей, главный из которых установлен на клиенте. Если клиенту понадобится какой-то тип из второстепенного удаленного модуля *.netmodule, CLR-среда по его требованию начнет немедленно загружать на локальную машину в специальное место, называемое кэшем загрузки (download cache) только соответствующий двоичный файл. В случае, если размер каждого модуля *.netmodule составляет 5 Мбайт, преимущество сразу же станет заметным (по сравнению с загрузкой одного файла размером в 15 Мбайт).
Другим преимуществом многофайловых сборок является то, что они позволяют создавать модули на различных языках программирования .NET. После компиляции отдельные модули все могут легко "объединяться" в одну логическую сборку с помощью компилятора C# командной строки.