М ы хоти м вас усл ышать
Мы приветствуем любые отзывы на эту книгу. П ожалуйста, оставьте ваши
комментарии и предл ожения в опр осе, к от орый нах одится п о адресу http://
m icr osoft.comjlear n i ngjboo ksurvey.
Ваше участие п ом ожет Microsoft Press с оздавать книги, к оторые с оответствуют
вашим запр осам и ожиданиям.
П Р И М ЕЧАН И Е
Мы надеемся, что вы оставите в о п росе nодробный отзыв. Если у вас есть
вопросы о нашей издательской программе, вскоре выnускаемых кн игах или
же издательстве в целом, мы рады ответить на них в Твиттере: http:jjtwitter.
c o m/ M i cr o s oftPr e s s. Для вопросов относител ь н о и здан и я и с n ол ьзуйте
только вышеуказан ную электронную n очту.
1 8 Введение
От издателя перевода
Ваши замечания, предл ожения и в опросы отправляйте по адресу электр онной
п очты comp@piter.com ( издательств о 􀅐питер•, компьютерная редакция) .
Мы будем рады узнать ваше мнение!
П одр обную информацию о наших книгах вы найдете на веб-сайте издательства
http:jjwww.piter.com.

Глава 1. Модел ь вы п ол нени якода в среде CLR

Microsoft .NET Framework представляет н овые концепции, технологии и термины.
Цель этой главы - дать обз ор архитектуры .NET Framework, познакомить
с н овыми техн ол огиями этой платф ормы и определить термины, с которыми вы
столкнетесь при раб оте с ней. Также в этой г лаве изложен процесс постр оения
прил ожения или набора распр остраняемых к омпонентов (файлов), которые
содержат типы (классы, структуры и т. п.), и затем объяснен о, как выполняется
прил ожение.
Компиляция исходного кода в уп равляемые модули
Итак, вы решили исп ольз овать .NET Framework как платформу разработки.
От личн о! Ваш первый шаг - определить вид с оздаваем ог о прил ожения или
к омпонента. Предп ол ожим, что этот в опрос уже решен, все спр оектир овано,
спецификации написаны и все г отово для начала разработки .
Теперь надо выбрать язык программир ования. И это непростая задача - ведь
у разных языков имеются разные в озможности. Например, с одной стор оны,
« неуправляемый к од�> CjC + + дает д оступ к системе на низком уровне. Вы
вправе распоряжаться памятью по своему усм отрению, создавать потоки и т. д.
Но с друг ой стороны, Visual Basic 6 позволяет очень быстро  строить пользовательские интерфейсы и легко управлять СОМ -объектами и базами данных.
Название среды - общеязыковая среда выполнения ( Common Language Runtime, C LR) - говорит само за себя : это среда выполнения, которая подходит
для разных языков программирования. Функциональные возможности
CLR доступны в любых языках пр ограммир ования, исп ользующих эту среду.
Например, при обработке ошиб ок среда вып олнения опирается на исключения,
а значит, во всех языках программир ования, использующих эту среду
выполнения, можно п олучать с ообщения об ошибках при пом ощи механизма
исключений. Или, например, среда выполнения позв оляет с оздавать пот ок,
а значит, во всех языках программир ования, исп ользующих эту среду, могут
создаваться пот оки.
Фактически во время вы полнения пр ограммы в среде CLR неизвестн о, на
как ом языке пр ограммир ования разработчик написал исходный к од. А это
значит, что м ожно выбрать любой язык пр ограм мир овани я, который позволяет
Ком п иляция исходного кода в управляемые модули 2 1
проще всего решить данную задачу. Разрабатывать программное обеспечение
можно на любом языке программирования, если используемый компилятор
этого языка предназначен для CLR.
Так в чем же тогда преимущества одного языка программирования перед
другим? Преимущества в компиляторах, во встроенном в них механизме
контроля синтаксиса и анализа корректного кода. Компиляторы проверяют
исходный код, убеждаются, что все написанное имеет некий смысл, и затем
генерируют код, описывающий решение данной задачи. Разные языки программирования
позволяют разрабатывать программное обеспечение, используя
различный синтаксис. Не стоит недооценивать значение выбора синтаксиса
языка программирования. Например, для математических или финансовых приложений
выражение мысли программиста на языке APL может сохранить много
дней работы по сравнению с применением в данной ситуации языка Perl.
Компания Microsoft разработала компиляторы для следующих языков программирования,
используемых на этой платформе: C++jCLI, С# ( произносится
«СИ шарп>> ), Visual Basic, JScript, J# ( компилятор языка Java) и ассемблер
Intermediate Language ( I L). Кроме M icrosoft, еще несколько компаний и университетов
создали компиляторы, предназначенные для среды выполнения
CLR. Мне известны компиляторы для APL, Caml, COBOL, Eiffel, Forth, Fortran,
Haskell, Lexico, LISP, LOGO, Lua, M ercury, ML, Mondrian, Oberon, Pascal, Perl,
Php, Prolog, Python, RPG, Scheme, Smalltalk и TcljTk.
Рисунок 1 . 1 иллюстрирует процесс компиляции файлов с исходным кодом.
Как видно из рисунка, исходный код программы может быть написан на л ю бом
языке, поддерживающем среду выполнения CLR. Затем соответствующий
компилятор проверяет синтаксис и анализирует исходный код программы.
В н е зависимости от типа используемого компилятора результатом компиляции
будет являться управляемый модуль (managed modu le) - стандартный
переносимый испол1/Яемый (portaЬle executaЬ!e, РЕ) файл 32 -разрядной ( РЕ32 )
или 64-разрядной Windows ( РЕ32 +), который требует для своего выполнения
Файлы
с исходным
кодом на С#
Уnравляемый модуль
(IL и метаданные)
Файлы
с исходны м
кодом на Basic
Файлы
с исходным
кодом на IL
Рис. 1 . 1 . Ком п иляция исходного кода в управляемые модули
22 Глава 1 . Модель выполнения кода в среде CLR
CLR. Кстати, управляемые сборки всегда используют преимущества функции
безопасности << предотвращения выполнения данных• ( D E P, Data Execution
Prevention) и технологию ASLR (Address Space Layout Optimization), применение
этих технологий повышает информационную безопасность программнога
обеспечения.
В табл. 1.1 описаны составные части управляемого модуля.
Таблица 1 .1 . Ч асти управляемого модул я
1 Часть Описание
Заголовок РЕ32 Стандартный заголовок РЕ-файла Windows, аналогичный заго-
или РЕ32+ ловку Соттоn Object File Forтat (COFF). Файл с заголовком
в формате РЕ32 может выполняться в 32- и 64-разрядной версиях
Windows, а с заголовком РЕ32+ - только в 64-разрядной. Заго-
ловок показывает тип файла: GUI, CUI или DLL, он также имеет
временную метку, показывающую, когда файл был собран. Для
модулей, содержащих только IL-код, основной объем информации
в заголовке РЕ32( +) игнорируется. В модулях, содержащих ма-
шинный код, этот заголовок содержит сведения о мащинном коде
Заголовок CLR Содержит информацию (интерпретируемую CLR и утилитами),
которая превращает этот моду ль в управляемый. Заголовок
включает нужную версию CLR, некоторые флаги, метку мета-
данных MethodDef точки входа в управляемый модуль ( метод
Ma i n ) , а также месторасположение/размер метаданных модуля,
ресурсов, строгого имени, некоторых флагов и пр.
Метаданные Каждый управляемый моду ль содержит таблицы метаданных.
Есть два основных вида таблиц - это таблицы, описывающие
типы данных и члены, определенные в исходном коде, и таблицы,
описывающие типы данных и члены, на которые имеются ссылки
в исходном коде
Код Interтediate Код, создаваемый компилятором при компиляции исходного кода.
Language ( I L) Впоследствии CLR компилирует IL в команды процессора
Компиляторы машинного кода производят код, ориентированный на конкретную
процессорную архитектуру, например х86, х64 или IA64. В отличие от
этого, все С LR-совместимые компиляторы генерируют IL-код. (Подробнее об
IL-коде рассказано далее в этой главе.) I L-код иногда называют управляемым.
(managed code), потому что CLR управляет его выполнением.
Каждый компилятор, предназначенный для CLR, помимо генерации IL-кода,
должен также создавать полные метадттые (metadata) для каждого управляемого
модуля. Если выражаться кратко, то метаданные - это набор таблиц
данных, описывающих то, что определено в модуле, например типы и их члены.
В метаданных также есть таблицы, указывающие, на что ссылается управляемый
модуль, например на импортируемые типы и их члены. Метаданные расширяют
возможности таких старых технологий, как библиотеки СОМ-типов
и файлы языка описания интерфейсов ( I nterface Definition Language, I D L ) .
Компиляция исходного кода в управляемые модули 23
Важно отметить, что метаданные CLR значительно более детальные. И, в отличие
от библиотек СОМ-типов и IDL-файлов, они всегда связаны с файлом,
содержащим I L-код. Фактически метаданные всегда встроены в тот же ЕХЕили
DLL-файл, что и код, так что их нельзя разделить. По причине того, что
компилятор генерирует метаданные и код одновременно и привязывает их
к конечному управляемому модулю, возможность рассинхронизации метаданных
и описываемого ими I L-кода исключена.
Метаданные имеют несколько применений. Перечислим некоторые из
них.
I:J Метаданные устраняют необходимость в заголовочных и библиотечных
файлах при компиляции, так как все сведения о типах/членах, на которые
есть ссылки, содержатся в файле с реализующим их IL-кодом. Компиляторы
могут читать метаданные прямо из управляемых модулей.
I:J Microsoft Visual Studio использует метаданные для облегчения написания
кода. Ее функция I nte l l i Sense анализирует метаданные и сообщает, какие
методы, свойства, события и поля предпочтительны в данном случае и какие
именно параметры требуются конкретным методам.
I:J В процессе верификации кода C L R использует метаданные, чтобы убедиться,
что код совершает только «безопасные• операции. ( Проверка кода
обсуждается далее.)
I:J Метаданные позволяют сериализовать поля объекта, затем передать эти
данные по сети на удаленный компьютер и там провести процесс десериализации,
восстановив объект и его состояние на удаленном компьютере.
I:J Метаданные позволяют сборщику мусора отслеживать жизненный цикл объектов.
При помощи метаданных сборщик мусора может определить тип объектов
и узнать, какие именно поля в них ссылаются на другие объекты.
В г лаве 2 метаданные описаны подробнее.
Языки программирования С#, Visual Basic, JScript, J# и IL-ассемблер всегда
создают модули, содержащие управляемый код ( I L ) и управляемые данные
(данные, поддерживающие сборку мусора). Для выполнения любого управляемого
модуля на машине конечного пользователя должна быть установлена CLR
(в составе .NET Framework) , так же как для выполнения приложений M F C
или Visual Basic 6 должны быть установлена библиотека классов M icrosoft
Foundation Class ( M F C ) или динамически подключаемые библиотеки Visual
Basic.
По умолчанию компилятор Microsoft С++ создает ЕХЕ- и D LL-файлы, которые
содержат неуправляемый код и неуправляемые данные. Для их выполнения
CLR не требуется. Однако если вызвать компилятор С++ с параметром / C L R
в командной строке, о н создаст управляемые модули, для работы которых необходимо
установить CLR. Компилятор С++ стоит особняком среди всех упомянутых
компиляторов производства M icrosoft - он единственный позволяет
24 Глава 1 . Модель выполнения кода в среде CLR
разработчикам писать как управляемый, так и неуправляемый код и встраивать
его в единый модуль. Это также единственный компилятор Microsoft, разрешающий
проrраммистам определять в исходном коде как управляемые, так
и неуправляемые типы данных. Это очень важное свойство, поскольку оно
позволяет разработчикам обращаться к существующему неуправляемому коду
на CjC + + из управляемого кода и постепенно, по мере необходимости, переходить
на управляемые типы.
Объединение уп равляемых модулей
в сборку
На самом деле среда C L R работает не с модулями, а со сборками. Сборка
(assemЬly) - это абстрактное понятие, осознание которого попачалу может
вызвать затруднения. Во-первых, это логическая группировка одного или нескольких
управляемых модулей или файлов ресурсов. Во-вторых, это самая
маленькая единица с точки зрения многократного использования, безопасности
и управления версиями. Сборка может состоять из одного или нескольких файлов
- все зависит от выбранных средств и компиляторов. В контексте среды
CLR сборку можно назвать кo.мno1te1tmoм.
О сборке довольно подробно рассказано в главе 2, а здесь достаточно подчеркнуть,
что эта концепция предлагает способ объединения группы файлов
в единую сущность.
Рисунок 1 .2 помогает понять суть сборки. На этом рисунке показано, что
некоторые управляемые модули и файлы ресурсов (или данных) создаются при
помощи инструментального средства. Это средство создает единственный файл
РЕ32( + ), который представляет логическую группировку файлов. Рассмотрим,
что происходит в случае, когда файл РЕ32( +) содержит блок данных, называемый
.ма1tuфесто.м (manifest) . Манифест - это просто один из наборов таблиц
метаданных. Эти таблицы описывают файлы, которые формируют сборку,
общедоступные экспортируемые типы, реализованные в файлах сборки, а также
относящиеся к сборке файлы ресурсов или данных.
По умолчанию компиляторы сами выполняют работу по иревращению
созданного управляемого модуля в сборку, то есть компилятор С# создает
управляемый моду ль с манифестом, указывающим, что сборка состоит только
из одного файла. Таким образом, в проектах, где есть только один управляемый
модуль и нет файлов ресурсов (или файлов данных), сборка и является управляемым
модулем, поэтому прилаrать дополнительных усилий по компоновке
приложения не нужно. В случае если необходимо сгруппировать несколько
файлов в сборку, потребуются дополнительные инструменты (например, компоновщик
сборок AL.exe) со своими параметрами командной строки. О них
подробно рассказано в главе 2 .
Управляемый модуль
( I L и метаданные)
Управляемый модуль
( I L и метаданные)
Файл ресурсов
( .jpeg, .gif, .html и т. п . )
Файл ресурсов
(.jpeg, .g if, . html и т. п . )
Инструмент, объединяющий
несколько управляющих
модулей и файлов ресурсов
в сборку
Ком пилятор С# (CSC.exe)
Ком п илятор Visual Basic
(VBC. exe)
Компоновщик сборок
(AL.exe)
Загрузка CLR 25
Сборка
( Манифест: описы вает набор
файлов е сборке)
Управляемый модуль
( I L и метаданные)
Управляемый модуль
( I L и м етаданные)
Файл ресурсов
(.jpeg, .gif, . html и т. п . )
Рис. 1 . 2. Объеди нение управляемых модулей в сборку
Сборка позволяет разделить логическое и физическое представления компонента,
поддерживающего многократное использование, безопасность и управление
версиями. Разбиение программнога кода и ресурсов на разные файлы
полностью определяется желаниями разработчика. Например, редко используемые
типы и ресурсы можно вынести в отдельные файлы сборки. Отдельные
файлы могут загружаться по запросу из И нтернета по мере необходимости
в процессе выполнения программы. Если некоторые файлы не потребуются,
то они не будут загружаться, что сохранит место на жестком диске и сократит
время установки программы. Сборки позволяют разбить на части процесс
развертывания файлов и в то же время рассматривать все файлы как одну
коллекцию.
Модули сборки также содержат сведения о других сборках, на которые они
ссылаются (в том числе номера их версий). Эти данные делают сборку са.моопи сывае.м.
ой (selfdescriЬing). Другими словами, среда CLR может определить по
порядку все прямые зависимости данной сборки, которые необходимы для ее
выполнения. Не нужно размещать никакой дополнительной информации ни
в системном реестре, ни в доменной службе AD D S {Active Directory Domain
Services) . Вследствие этого развертывать сборки гораздо проще, чем неуправляемые
компоненты.
Загрузка CLR
Каждая создаваемая сборка представляет собой либо исполняемое приложение,
либо библиотеку DLL, содержащую набор типов (компонентов) для использования
в исполняемом приложении. Разумеется, среда CLR отвечает за
26 Глава 1 . М одел ь выполнения кода в среде CLR
управление исполнением кода. Это значит, что на компьютере, выполняющем
данное приложение, должна быть установлена платформа .NET Framework.
В компании Microsoft был создан дистрибутивный пакет .NET Framework для
свободного распространения, который вы можете бесплатно поставлять своим
клиентам. Некоторые версии операционной системы семейства Windows поставляются
с уже установленной платформой .NET Framework.
Для того чтобы понять, установлена ли платформа .NET F ramework
на компьютере, нужно попробовать найти файл M S C o r E E . d l l в каталоге
%SystemRoot%\system 3 2 . Если он есть, то платформа .NET Framework установлена.
Однако на одном компьютере может быть установлено одновременно
несколько версий .NET Framework. Чтобы определить, какие именно версии
установлены, проверьте следующий подраздел системного реестра:
HKEY_LOCAL_MACH I N E \ SOFTWAR E \ M i c rosoft \ N ET Framewo r k Setup\NDP
В комплекте .NET Framework SDK компания Microsoft поставляет утилиту
командной строки CLRVer.exe, позволяющую узнать, какие версии CLR установлены
на машине, а также, какая именно версия среды CLR используется
текущими процессами. Для этого нужно указать параметр -а 1 1 или идентификатор
интересующего процесса.
Прежде чем мы перейдем к загрузке среды CLR, поговорим поподробнее
об особенностях 32- и 64-разрядных версий операционной системы Windows.
Если сборка содержит только управляемый код с контролем типов, она должна
одинаково хорошо работать на обеих версиях системы. Дополнительной модификации
исходного кода не требуется. Созданный компилятором готовый
Е Х Е - или D L L-файл будет выполняться как на 32-разрядной Windows, так
и на версиях х64 и IA64 64-разрядной Windows ! Другими словами, один и тот
же файл будет работать на любом компьютере с установленной платформой
.NET Framework.
В исключительно редких случаях разработчикам понадобится писать код,
совместимый только с какой-то конкретной версией Windows. Обычно это
требуется при работе с небезопасны.м кодом (unsafe code) или для взаимодействия
с неуправляемым кодом, ориентированным на конкретную процессорную
архитектуру. Для таких случаев у компилятора С# предусмотрен параметр
командной строки 1 р 1 а t fo rm. Этот параметр позволяет указать конкретную
версию целевой платформы, на которой планируется работа данной сборки:
архитектуру х86, использующую только 32-разрядную систему Windows, архитектуру
х64, использующую только 64-разрядную операционную систему
Windows, или архитектуру Intel ltanium, использующую только 64-разрядную
систему Windows. Если не указать платформу, компилятор задействует значение
по умолчанию a nycpu, которое означает, что сборка может выполняться на
любой из данных операционных систем Windows. Пользователи Visual Studio
могут указать целевую платформу в списке P latform Targ et на вкладке Bui ld
окна свойств проекта (рис. 1 .3 ) .
СН....оl
Corwhtюnal comptt.Ьott tymhoЬ
OrrfW DEВUG comunt
􀫲ПAa «МUtJn�
"-form tatg.t't
Alow uns.tle cock
k􀫳*fюc:e PICtи .J Opьn111t cock
􀫰 lffon and W1fNn91
Cod< An.oy.o Watrwtg f«Yef-
см. с...ьиь 5upprнswv�·
Trt. Wif􀫴 M fffOf'
...
.. ,
·􀫱
[! ·1
Загрузка CLR 27
Рис. 1 .3 . Определение целевой платформы средствами
Visual Studio
В зависимости от указанной целевой платформы С# генерирует заголовок -
РЕ32 или РЕ32+, а также указывает требуемую процессорную архитектуру
(или информирует о независимости от архитектуры ) в заголовке. Для анализа
заголовочной информации, созданной компилятором в управляемом модуле,
Microsoft предоставляет две утилиты - D u m p B i n .exe и CorFiags.exe.
При запуске исполняемого файла Windows анализирует заголовок ЕХЕфайла
для определения того, какое именно адресное пространство необходимо
для его работы - 32- или 64-разрядное. Файл с заголовком РЕ32 может выполняться
в адресном пространстве любого из указанных двух типов, а файлу
с заголовком РЕ32+ требуется 64-разрядное пространство. Windows также
проверяет информацию о процессорной архитектуре на предмет совместимости
с имеющейся конфигурацией. Наконец, 64-разрядные версии Windows поддерживают
технологию выполнения 32-разрядных приложений в 64-разрядной
среде, которая называется WoW64 (Windows on Windows64 ) . Она даже позволяет
выполнять 32 -разрядные приложения на машине с процессаром Itanium
за счет эмуляции команд х86, но за это приходится расплачиваться снижением
производительности.
Таблица 1 .2 иллюстрирует две важные вещи. Во-первых, в ней показан тип
получаемого управляемого модуля при указании разных параметров / p l a t fo rm
командной строки компилятора С#. Во-вторых, в ней представлены режимы
выполнения приложений в различных версиях Windows.
28 Глава 1 . М одель выполнения кода в среде CLR
Таблица 1 .2. Вл и я н и е заданного значения параметра fplatform
на получае м ы й модуль и режим вы полнения
Значение Тип x86 Windows x64 Windows IA64 Windows
параметра выходноrо
/platform управляемоrо
модуля
anycpu (по РЕ32jнеза- Выполняется Выполняется Выполняется
умолчанию) висимый от как 32-разрядное как 64-разрядное как 32-разрядное
платформы приложение приложение пр ил ожени е
х86 РЕ32/х86 Выполняется Выполняется Выполняется как
как 32-разрядное как WoW64- WoW64- прило-
приложение пр ил ожени е жение
х64 РЕ32+/х64 Не выполняется Выполняется Не выполняется
как 64-разрядное
приложение
Itanium РЕ32+ /Itanium Не выполняется Не выполняется Выполняется
как 64-разрядное
пр ил ожени е
После анализа заголовка ЕХЕ-файла для выяснения того, какой процесс
необходимо запустить - 3 2 - , 64-разрядный или WoW64, - Windows загружает
в адресное пространство процесса соответствующую (х86, х64 или IA64)
версию библиотеки MSCor E E . d l l . В системе Windows версии х86 одноименная
версия MSCorEE . d l l хранится в каталоге C:\Wi ndows\System32. В системах х64
и IA64 версия х86 библиотеки находится в каталоге C:\Windows\SysWow64,
а 64-разрядная версия MSCor E E . d l l (х64 или IA64) размещается в каталоге С:\
W i n d ows\System 3 2 ( это сделано из соображений обратной совместимости) .
Далее основной поток вызывает определенный в библиотеке MSCorEE . d l l метод,
который инициализирует C LR, загружает сборку ЕХЕ, а затем вызывает
ее метод M a i n, в котором содержится точка входа. На этом процедура запуска
управляемого приложения считается завершенной1•
П Р И М ЕЧ А Н И Е
С б о р к и , созда н н ы е п р и п о м о щ и в е р с и й 7. 0 и 7. 1 ко м п илятора С # от
Microsoft, содержат заголовок РЕЗ2 и не зависят от архитектуры процессора.
Тем н е менее во время выполнения среда CLR считает их совмести м
ы м и тол ько с архитектурой х86. Это повышает вероятность макси мально
ко р р е к т н о й работы в 6 4 - разрядной с реде, так как и с п ол н я е м ы й файл
загружается в режиме WoW6 4 , кото р ы й о б е с п е ч и вает п р о цессу среду,
м а кс и м а л ь н о п р и бл иже н ну ю к существующей в 32- разрядн о й в е р с и и
х 8 6 Windows.
1 Программный код может запросить переменную окружения I s 64Bi t0perati ngSystem для того, чтобы
определить, выполняется ли данная программа в 64-разрядной системе Windows, а также запросить
переменную окружения I s 64B i t P rocess, чтобы определить, выполняется ли данная программа
в 64-разрядном адресном пространстве.
Исполн ение кода сборки 29
Когда неуправляемое приложение вызывает LoadLibrary, Windows автоматически
загружает и инициализирует CLR (если это еще не сделано) для
обработки содержащегося в сборке кода. Ясно, что в такой ситуации предполагается,
что процесс запущен и работает, и это сокращает область применимости
сборки. В частности, управляемая сборка, скомпилированная с параметром
/р l atfo rm : х86, не сможет загрузиться в 64-разрядный процесс, а исполняемый
файл с таким же параметром загрузится в режиме WoW64 на компьютере
с 64-разрядной Windows.
Исполнение кода сборки
Как говорилось ранее, управляемые модули содержат метаданные и программный
код, написанный на языке IL. Это не зависящий от процессара машинный
язык, разработанный компанией Microsoft после консультаций с несколькими
коммерческими и академическими организациями, специализирующимися
на разработке языков и компиляторов. I L - язык более высокого уровня по
сравнению с большинством других машинных языков. Он позволяет работать
с объектами и имеет команды для создания и инициализации объектов, вызова
виртуальных методов и непосредственно го манипулирования элементами
массивов. В нем даже есть команды выбрасывания и перехвата исключений для
обработки ошибок. IL можно рассматривать как объектно-ориентированный
машинный язык.
Обычно разработчики программируют на высокоуровневых языках, таких
как С #, C++/CLI или Visual Basic. Компиляторы этих языков генерируют
I L-код. Однако такой код может быть написан и на языке ассемблера, так,
Microsoft предоставляет ассемблер IL ( I LAs m . exe) , а также дизассемблер I L
( I LDasm.exe) .
Имейте в виду, что любой язык высокого уровня, скорее всего, использует
лишь часть возможностей, предоставляемых CLR. При этом язык ассемблера I L
открывает доступ ко всем возможностям C L R . В случае если выбранный вами
язык программирования не дает доступа именно к тем функциям CLR, которые
необходимы, можно написать часть программнога кода на языке ассемблера I L
или на другом языке программирования, позволяющем их задействовать.
Единственный способ узнать о возможностях CLR, доступных при использовании
конкретного языка, - изучить соответствующую документацию.
В этой книге сделан акцент на возможностях среды CLR и на том, какие из
этих возможностей доступны при программировании на С#. Можно сделать
предположение, что в других книгах и статьях среда CLR рассмотрена с точки
зрения других языков и разработчики получат представление лишь о тех ее
функциях, которые доступны при использовании описанных там языков. По
крайней мере, если выбранный язык решает поставленные задачи, такой подход
не так уж плох.
30 Глава 1 . М одел ь выполнения кода в среде CLR
В Н И МА Н И Е
Я думаю, что возможность легко переключаться между я з ы ками п р и их
тесной и нтеграции - чудесное качество CLR. К сожал ению, я также практически
уверен, что разработч ики часто будут проходить мимо нее. Такие
языки, как С # и Visual Basic, п рекрасно подходят для программи рования
ввода- вы вода . Язык APL (д Programmiпg Laпguage) - замечательный язык
для и нженерных и финансовых расчетов . Среда CLR позволяет написать
на С# часть приложе н и я , отвечающую за в вод- вывод, а инженерные расчеты
- на я з ы ке APL. Среда CLR предлагает беспрецеде нтн ы й уровень
и нтеграции этих языков, и во многих проектах стоит серьезно задуматься
об испол ьзован и и одновременно нескол ьких языков.
Для выполнения какого-либо метода его I L-код должен быть преобразован
в машинные команды. Этим занимается J IТ-компилятор (Just- ln-Time) среды
CLR.
На рис. 1 .4 показано, что происходит при первом обращении к методу.
Консоль
Уnравляемый ЕХЕ-файп stat1c void Wr1teL1ne ( )
1 JIТCo•pile r 1 static void Hai n ( ) { - h
}
Console . W r i t e l i n e ( " H e l l o " ) ;
Console . W r i t e l i n e ( "Goodbye· ) ; l static void Wr1tel1ne ( st r1 ng ) Н J IТCoapi l e r 1
) (прочие члены) 1 . . . 1
MSCorEE.dll
JП1. Co111p1ler function { В сборке, реализующей данный тип {Console).
найти в метаданных вызываемый метод {Writeli ne).
:1' Команды
процессара
"'--2. Взять из метаданных IL-код для этого метода. L/ 3. Выделить блок памяти.
4. Скомпилировать IL-код в команды п роцессара
и сохранить процессорный код в памяти,
выделенной на этапе 3.
5. Измен ить точку входа метода в табл ице типа,
чтобы она указывала на блок памяти.
выделенный на этапе 3.
б. Передать управление процессарному коду,
} содержащемуся в выделенном блоке памяти.
Рис. 1 .4. Первый вызов метода
1
Исполн ение кода сборки 3 1
Непосредственно перед исполнением метода Ma i n среда C L R находит все
типы данных, на которые ссылается программный код метода Ma i n . При этом
CLR выделяет внутренние структуры данных, используемые для управления
доступом к типам, на которые есть ссылки. На рисунке 1 .4 метод Ma i n ссылается
на единственный тип - Conso 1 e, и среда CLR выделяет единственную внутреннюю
структуру. Эта внутренняя структура данных содержит по одной записи
для каждого метода, определенного в типе Conso 1 е. Каждая запись содержит
адрес, по которому можно найти реализацию метода. При инициализации этой
структуры CLR заносит в каждую запись адрес внутренней недокументированной
функции, содержащейся в самой среде CLR. Эта функция называется
J I ТComp i 1 er.
Когда метод Ma i n первый раз обращается к методу Wri t e l i ne, вызывается
функция J I ТCompi 1 er. Она отвечает за компиляцию I L-кода вызываемого метода
в собственные команды процессора. Поскольку IL-код компилируется непосредственно
перед выполнением ( 􀅐j ust in time􀃘 ), этот компонент CLR часто
называют ]IT-mepoм, или ]IТ-комnWlЯтором.
П Р И М ЕЧАНИ Е
Если приложение исполняется в х86 версии Windows и л и в режиме WoW64,
J IТ- компилятор генерирует команды для х86 архитектуры . Для приложе н и й ,
выполня ющихся к а к 54- разрядные в в е р с и и х 6 4 или ltan i u m ОС Windows ,
J IТ- компилятор генери рует соответственно команды для архитектуры х64
или IA64.
Функции J I TComp i 1 er известен вызываемый метод и тип, в котором он
определен. J I ТCompi 1 er ищет в метаданных соответствующей сборки I L-код
вызываемого метода. Затем J I ТCompi 1 er проверяет и компилирует IL- код в собственные
машинные команды, которые сохраняются в динамически выделенном
блоке памяти. После этого J I TComp i 1 er возвращается к структуре внутренних
данных типа, созданной средой CLR, и заменяет адрес вызываемого метода
адресом блока памяти, содержащего готовые машинные команды. В завершение
J I ТCompi 1 er передает управление коду в этом блоке памяти. Этот программный
код является реализацией метода Wri tel i ne (вариант этого метода с параметром
St ri ng). Из этого метода управление возвращается в метод Ma i n, который продолжает
выполнение в обычном порядке.
Рассмотрим повторное обращение метода Ма i n к методу W r i tel i ne. К этому
моменту код метода W r i tel i ne уже проверен и скомпилирован, так что обращение
к блоку памяти производится, минуя вызов J I ТCompi 1 e r. Отработав, метод
Wri tel i ne возвращает управление методу Ма i n. На рис. 1 .5 показано, как выглядит
ситуация при повторном обращении к методу Wri tel i ne.
Снижение производительности наблюдается только при первом вызове метода.
Все последующие обращения выполняются <<На максимальной скорости􀃘 ,
потому что повторная верификация и компиляция не производятся.
32 Глава 1 . М одел ь выполнения кода в среде CLR
Ynpaanteмыii ЕХЕ·файп
stat1c vo1d Mai n ( ) { Console . Wr1teL1ne( " H e l l o " ) ;
} Console. Wr1 tel1ne( "Goodbye · ) ; - 1
Консоп ..
stat1c vo1d Wr1tel1ne ( )
1 JПCo• p 1 l e r 1
stat1c vo1d W r 1 tel1ne ( st r1 n g )
�l Native J "--- (nрочие члены)
1 .. . 1
Рис. 1 . 5 . Повторный вызов метода
11\ Команды
n роцессора 1 J
] IТ-компилятор хранит машинные команды в динамической памяти. Это
значит, что скомпилированный код уничтожается по завершении работы приложения.
Для повторного вызова приложения или для параллельного запуска его
второго экземпляра (в другом процессе операционной системы) ]IТ-компилятору
придется заново скомпилировать IL-код в машинные команды.
Для большинства приложений снижение производительности, связанное
с работой JIT -компилятора, незначительно. Большинство приложений раз за
разом обращается к одним и тем же методам. На производительности это сказывается
только один раз во время выполнения приложения. К тому же больше
времени занимает выполнение самого метода, а не обращение к нему.
Необходимо также знать, что ]IТ- компилятор среды CLR оптимизирует
машинный код аналогично компилятору неуправляемого кода С++. И опять
же: создание оптимизированного кода занимает больше времени, но при выполнении
он гораздо производительнее, чем неоптимизированный.
Есть два параметра компилятора С#, влияющих на оптимизацию кода, - 1 opt i mi ze и 1 debug. В следующей таблице по казан о их влияние на качество
Исполнение кода сборки 33
IL-кода, созданного компилятором С#, и машинного кода, сгенерированного
]IT-компилятором.
Параметры компилятора Качество IL-кода Качество м аwинного
компилятора JIТ-кода
/ o pt i mi z e - / debu g - Неоптимизированный Оптимизированный
(по умолчанию)
/opt i mi z e - / debug ( + / fu l l / pdbon l y ) Неоптимизированный Неоптимизированный
/ opt i mi ze+ / debug ( - / + / f u l l / pbdon l y ) Оптимизированный Оптимизированный
С параметром 1 o pt i mi z e - компилятор С# генерирует неоптимизированный
IL-код, содержащий множество пустых команд (no-operation, NOP). Эти команды
предназначены для поддержки функции 4редактирования с продолжением
выполнения􀃘 (edit-and-continue) в Visual Studio во время процесса отладки.
Они также упрощают процесс отладки, позволяя расставлять точки останова
(breakpoints) на управляющих командах, таких как fo r, w h i l е , do, i f, el s e , а
также блоках t ry, catch и fi na l l y . Во время оптимизации I L-кода компилятор
С# удаляет эти посторонние команды, усложняя процесс отладки кода, но зато
оптимизируя поток управления программой. Кроме того, возможно, некоторые
оценочные функции не выполняются во время отладки. Однако IL-код меньше
по размерам, и это уменьшает результирующий размер ЕХЕ- или DLL-файлов;
кроме того, I L-код легче читать тем, кто обожает исследовать IL-код, пытаясь
понять, что именно породил компилятор (например, мне).

Гл а в а 2 . Ком поновка ,
уп а ко вка , разверты ван и е
и адм и н и стр и рова н и е
- п риложе н и и и ти пов