Также в этой главе изложен процесс построения приложения или набора распространяемых компонентов (файлов), которые содержат типы (классы, структуры и т. п.), и затем объяснено, как выполняется приложение.
Компиляция исходного кода в управляемые модули
Итак, вы решили использовать .NET Framework как платформу разработки. От лично! Ваш первый шаг - определить вид создаваемого приложения или компонента. Предположим, что этот вопрос уже решен, все спроектировано, спецификации написаны и все готово для начала разработки.
Теперь надо выбрать язык программирования. И это непростая задача - ведь у разных языков имеются разные возможности. Например, с одной стороны, «неуправляемый код» C/C++ дает доступ к системе на низком уровне. Вы вправе распоряжаться памятью по своему усмотрению, создавать потоки и т. д. Но с другой стороны, Visual Basic 6 позволяет очень быстро строить пользовательские интерфейсы и легко управлять СОМ-объектами и базами данных.
Название среды - общеязыковая среда выполнения (Common Language Runtime, CLR) - говорит само за себя: это среда выполнения, которая под ходит для разных языков программирования. Функциональные возможности CLR доступны в любых языках программирования, использующих эту среду. Например, при обработке ошибок среда выполнения опирается на исключения, а значит, во всех языках программирования, использующих эту среду выполнения, можно получать сообщения об ошибках при помощи механизма исключений. Или, например, среда выполнения позволяет создавать поток, а значит, во всех языках программирования, использующих эту среду, могут создаваться потоки.
Фактически во время выполнения программы в среде CLR неизвестно, на каком языке программирования разработчик написал исходный код. А это значит, что можно выбрать любой язык программирования, который позволяет проще всего решить данную задачу. Разрабатывать программное обеспечение можно на любом языке программирования, если используемый компилятор этого языка предназначен для CLR.
Так в чем же тогда преимущества одного языка программирования перед другим? Преимущества в компиляторах, во встроенном в них механизме контроля синтаксиса и анализа корректного кода. Компиляторы проверяют исходный код, убеждаются, что все написанное имеет некий смысл, и затем генерируют код, описывающий решение данной задачи. Разные языки программирования позволяют разрабатывать программное обеспечение, используя различный синтаксис. Не стоит недооценивать значение выбора синтаксиса языка программирования. Например, для математических или финансовых приложений выражение мысли программиста на языке APL может сохранить много дней работы по сравнению с применением в данной ситуации языка Perl.
Компания Microsoft разработала компиляторы для следующих языков программирования, используемых на этой платформе: C++/CLI, С# (произносится «СИ шарп» ), Visual Basic, JScript, J# (компилятор языка Java) и ассемблер Intermediate Language (IL). Кроме Microsoft, еще несколько компаний и университетов создали компиляторы, предназначенные для среды выполнения CLR. Мне известны компиляторы для APL, Caml, COBOL, Eiffel, Forth, Fortran, Haskell, Lexico, LISP, LOGO, Lua, Mercury, ML, Mondrian, Oberon, Pascal, Perl, Php, Prolog, Python, RPG, Scheme, Smalltalk и Tcl/Tk.
Рисунок 1.1 иллюстрирует процесс компиляции файлов с исходным кодом. Как видно из рисунка, исходный код программы может быть написан на любом языке, поддерживающем среду выполнения CLR. Затем соответствующий компилятор проверяет синтаксис и анализирует исходный код программы. Вне зависимости от типа используемого компилятора результатом компиляции будет являться управляемый модуль (managed module) - стандартный переносимый исполняемый (portable executable, РЕ) файл 32-разрядной (РЕ32) или 64-разрядной Windows (РЕ32+), который требует для своего выполнения CLR.
Рис. 1. 1. Компиляция исходного кода в управляемые модули
Кстати, управляемые сборки всегда используют преимущества функции безопасности «предотвращения выполнения данных» (DEP, Data Execution Prevention) и технологию ASLR (Address Space Layout Optimization), применение этих технологий повышает информационную безопасность программного обеспечения.
В табл. 1.1 описаны составные части управляемого модуля.
Таблица 1.1. Части управляемого модуля
Часть |
Описание |
Заголовок РЕ32 или РЕ32+ |
Стандартный заголовок РЕ-файла Windows, аналогичный заголовку Common Object File Format (COFF). Файл с заголовком в формате РЕ32 может выполняться в 32- и 64-разрядной версиях Windows, а с заголовком РЕ32+ - только в 64-разрядной. Заголовок показывает тип файла: GUI, CUI или DLL, он также имеет временную метку, показывающую, когда файл был собран. Для модулей, содержащих только IL-код, основной объем информации в заголовке РЕ32(+) игнорируется. В модулях, содержащих машинный код, этот заголовок содержит сведения о машинном коде |
Заголовок CLR |
Содержит информацию (интерпретируемую CLR и утилитами), которая превращает этот моду ль в управляемый. Заголовок включает нужную версию CLR, некоторые флаги, метку метаданных MethodDef точки входа в управляемый модуль (метод Main), а также месторасположение/размер метаданных модуля, ресурсов, строгого имени, некоторых флагов и пр. |
Метаданные |
Каждый управляемый модуль содержит таблицы метаданных. Есть два основных вида таблиц - это таблицы, описывающие типы данных и члены, определенные в исходном коде, и таблицы, описывающие типы данных и члены, на которые имеются ссылки в исходном коде |
Код IntermediateLanguage (IL) |
Код, создаваемый компилятором при компиляции исходного кода. Впоследствии CLR компилирует IL в команды процессора
|
Компиляторы машинного кода производят код, ориентированный на конкретную процессорную архитектуру, например х86, х64 или IA64. В отличие от этого, все СLR-совместимые компиляторы генерируют IL-код. (Подробнее об IL-коде рассказано далее в этой главе.) IL-код иногда называют управляемым. (managed code), потому что CLR управляет его выполнением.
Каждый компилятор, предназначенный для CLR, помимо генерации IL-кода, должен также создавать полные метаданные (metadata) для каждого управляемого модуля. Если выражаться кратко, то метаданные - это набор таблиц данных, описывающих то, что определено в модуле, например типы и их члены. В метаданных также есть таблицы, указывающие, на что ссылается управляемый модуль, например на импортируемые типы и их члены. Метаданные рас ширяют возможности таких старых технологий, как библиотеки СОМ-типов и файлы языка описания интерфейсов (Interface Definition Language, IDL). Важно отметить, что метаданные CLR значительно более детальные. И, в отличие от библиотек СОМ-типов и IDL-файлов, они всегда связаны с файлом, содержащим IL-код. Фактически метаданные всегда встроены в тот же ЕХЕ или DLL-файл, что и код, так что их нельзя разделить. По причине того, что компилятор генерирует метаданные и код одновременно и привязывает их к конечному управляемому модулю, возможность рассинхронизации метаданных и описываемого ими IL-кода исключена.
Метаданные имеют несколько применений. Перечислим некоторые из них.
· Метаданные устраняют необходимость в заголовочных и библиотечных файлах при компиляции, так как все сведения о типах/членах, на которые есть ссылки, содержатся в файле с реализующим их IL-кодом. Компиляторы могут читать метаданные прямо из управляемых модулей.
· Microsoft Visual Studio использует метаданные для облегчения написания кода. Ее функция IntelliSense анализирует метаданные и сообщает, какие методы, свойства, события и поля предпочтительны в данном случае и какие именно параметры требуются конкретным методам.
· В процессе верификации кода CLR использует метаданные, чтобы убедиться, что код совершает только безопасные операции. (Проверка кода обсуждается далее.)
· Метаданные позволяют сериализовать поля объекта, затем передать эти данные по сети на удаленный компьютер и там провести процесс десериализации, восстановив объект и его состояние на удаленном компьютере.
· Метаданные позволяют сборщику мусора отслеживать жизненный цикл объектов. При помощи метаданных сборщик мусора может определить тип объектов и узнать, какие именно поля в них ссылаются на другие объекты.
В главе 2 метаданные описаны подробнее.
Языки программирования С#, Visual Basic, JScript, J# и IL-ассемблер всегда создают модули, содержащие управляемый код (IL) и управляемые данные (данные, поддерживающие сборку мусора). Для выполнения любого управляемого модуля на машине конечного пользователя должна быть установлена CLR (в составе .NET Framework), так же как для выполнения приложений MFC или Visual Basic 6 должны быть установлена библиотека классов Microsoft Foundation Class (MFC) или динамически подключаемые библиотеки Visual Basic.
По умолчанию компилятор Microsoft С++ создает ЕХЕ- и DLL-файлы, которые содержат неуправляемый код и неуправляемые данные. Для их выполнения CLR не требуется. Однако если вызвать компилятор С++ с параметром /CLR в командной строке, он создаст управляемые модули, для работы которых не обходимо установить CLR. Компилятор С++ стоит особняком среди всех упомянутых компиляторов производства Microsoft - он единственный позволяет разработчикам писать как управляемый, так и неуправляемый код и встраивать его в единый модуль. Это также единственный компилятор Microsoft, разрешающий программистам определять в исходном коде как управляемые, так и неуправляемые типы данных. Это очень важное свойство, поскольку оно позволяет разработчикам обращаться к существующему неуправляемому коду на C/C++ из управляемого кода и постепенно, по мере необходимости, пере ходить на управляемые типы.
Объединение управляемых модулей в сборку
На самом деле среда CLR работает не с модулями, а со сборками. Сборка (assembly) - это абстрактное понятие, осознание которого поначалу может вызвать затруднения. Во-первых, это логическая группировка одного или не скольких управляемых модулей или файлов ресурсов. Во-вторых, это самая маленькая единица с точки зрения многократного использования, безопасности и управления версиями. Сборка может состоять из одного или нескольких файлов - все зависит от выбранных средств и компиляторов. В контексте среды CLR сборку можно назвать компонентом.
О сборке довольно подробно рассказано в главе 2, а здесь достаточно подчеркнуть, что эта концепция предлагает способ объединения группы файлов в единую сущность.
Рисунок 1.2 помогает понять суть сборки. На этом рисунке показано, что некоторые управляемые модули и файлы ресурсов (или данных) создаются при помощи инструментального средства. Это средство создает единственный файл РЕ32(+), который представляет логическую группировку файлов. Рассмотрим, что происходит в случае, когда файл РЕ32(+) содержит блок данных, называемый манифестом (manifest). Манифест - это просто один из наборов таблиц метаданных. Эти таблицы описывают файлы, которые формируют сборку, общедоступные экспортируемые типы, реализованные в файлах сборки, а также относящиеся к сборке файлы ресурсов или данных.
По умолчанию компиляторы сами выполняют работу по превращению созданного управляемого модуля в сборку, то есть компилятор С# создает управляемый моду ль с манифестом, указывающим, что сборка состоит только из одного файла. Таким образом, в проектах, где есть только один управляемый модуль и нет файлов ресурсов (или файлов данных), сборка и является управляемым модулем, поэтому прилагать дополнительных усилий по компоновке приложения не нужно. В случае если необходимо сгруппировать несколько файлов в сборку, потребуются дополнительные инструменты (например, компоновщик сборок AL.exe) со своими параметрами командной строки. О них подробно рассказано в главе 2.
Рис. 1.2. Объединение управляемых модулей в сборку
Сборка позволяет разделить логическое и физическое представления компонента, поддерживающего многократное использование, безопасность и управление версиями. Разбиение программного кода и ресурсов на разные файлы полностью определяется желаниями разработчика. Например, редко используемые типы и ресурсы можно вынести в отдельные файлы сборки. Отдельные файлы могут загружаться по запросу из Интернета по мере необходимости в процессе выполнения программы. Если некоторые файлы не потребуются, то они не будут загружаться, что сохранит место на жестком диске и сократит время установки программы. Сборки позволяют разбить на части процесс развертывания файлов и в то же время рассматривать все файлы как одну коллекцию.
Модули сборки также содержат сведения о других сборках, на которые они ссылаются (в том числе номера их версий). Эти данные делают сборку самоописываемой (selfdescrinbing). Другими словами, среда CLR может определить по порядку все прямые зависимости данной сборки, которые необходимы для ее выполнения. Не нужно размещать никакой дополнительной информации ни в системном реестре, ни в доменной службе AD DS (Active Directory Domain Services). Вследствие этого развертывать сборки гораздо проще, чем неуправляемые компоненты.