Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

Кроме того, оба инструмента версии 5.0.0 включены в папку

Chapter_19
внутри хранилища GitHub для настоящей книги.

Хотя и верно утверждение о том, что построением полного приложения .NET Core прямо на CIL занимаются лишь немногие программисты (если вообще такие есть), изучение этого языка все равно является чрезвычайно интересным занятием. Попросту говоря, чем лучше вы понимаете грамматику CIL, тем больше способны погрузиться в мир расширенной разработки приложений .NET Core. Обратившись к конкретным примерам, можно утверждать, что разработчики, разбирающиеся в CIL, обладают следующими навыками.

• Умеют дизассемблировать существующую сборку .NET Core, редактировать код CIL в ней и заново

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

• Умеют строить динамические сборки с применением пространства имен

System.Reflection.Emit
. Данный API-интерфейс позволяет генерировать в памяти сборку .NET Core, которая дополнительно может быть сохранена на диск. Это полезный прием для разработчиков инструментов, которым необходимо генерировать сборки на лету.

• Понимают аспекты CTS, которые не поддерживаются высокоуровневыми управляемыми языками, но существуют на уровне CIL. На самом деле CIL является единственным языком .NET Core, который позволяет получать доступ ко всем аспектам CTS. Например, за счет использования низкоуровневого кода CIL появляется возможность определения членов и полей глобального уровня (которые не разрешены в С#).

Ради полной ясности нужно еще раз подчеркнуть, что овладеть мастерством работы с языком C# и библиотеками базовых классов .NET Core можно и без изучения деталей кода CIL. Во многих отношениях знание CIL аналогично знанию языка ассемблера программистом на С (и C++).Те, кто разбирается в низкоуровневых деталях, способны создавать более совершенные решения поставленных задач и глубже понимают лежащую в основе среду программирования (и выполнения). Словом, если вы готовы принять вызов, тогда давайте приступим к исследованию внутренних деталей CIL.

На заметку! Имейте в виду, что эта глава не планировалась быть всеобъемлющим руководством по синтаксису и семантике CIL.

Директивы, атрибуты и коды операций CIL

Когда вы начинаете изучение низкоуровневых языков, таких как CIL, то гарантированно встретите новые (и часто пугающие) названия для знакомых концепций. Например, к этому моменту приведенный ниже набор элементов вы почти наверняка посчитаете ключевыми словами языка C# (и это правильно):

{new, public, this, base, get, set, explicit, unsafe, enum, operator, partial}

Тем не менее, внимательнее присмотревшись к элементам набора, вы сможете заметить, что хотя каждый из них действительно является ключевым словом С#, он имеет радикально отличающуюся семантику Скажем, ключевое слово

enum
определяет производный от
System.Enum
тип, а ключевые слова
this
и
base
позволяют ссылаться на текущий объект и его родительский класс. Ключевое слово
unsafe
применяется для установления блока кода, который не может напрямую отслеживаться средой CLR, а ключевое слово
operator
дает возможность создать скрытый (специально именованный) метод, который будет вызываться, когда используется специфическая операция C# (такая как знак "плюс").

По разительному контрасту с высокоуровневым языком вроде C# в CIL не просто определен общий набор ключевых слов сам по себе. Напротив, набор лексем, распознаваемых компилятором CIL, подразделяется на следующие три обширные категории, основываясь на их семантике:

• директивы CIL;

• атрибуты CIL;

• коды операций CIL.

Лексемы CIL каждой категории выражаются с применением отдельного синтаксиса и комбинируются для построения допустимой сборки .NET Core.

Роль

директив CIL

Прежде всего, существует набор хорошо известных лексем CIL, которые используются для описания общей структуры сборки .NET Core. Такие лексемы называются директивами. Директивы CIL позволяют информировать компилятор CIL о том, каким образом определять пространства имен, типы и члены, которые будут заполнять сборку.

Синтаксически директивы представляются с применением префикса в виде точки (

.
), например,
.namespace
,
.class
,
.publickeytoken
,
.override
,
.method
,
.assembly
и т.д. Таким образом, если в файле с расширением
*.il
(общепринятое расширение для файлов кода CIL) указана одна директива
.namespace
и три директивы
.class
, то компилятор CIL сгенерирует сборку, в которой определено единственное пространство имен .NET Core, содержащее три класса .NET Core.

Роль атрибутов CIL

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

.class
может быть снабжена атрибутом
public
(для установления видимости типа), атрибутом
extends
(для явного указания базового класса типа) и атрибутом
implements
(для перечисления набора интерфейсов, поддерживаемых данным типом).

На заметку! Не путайте атрибут .NET Core (см. главу 17) и атрибут CIL, которые являются двумя совершенно разными понятиями.

Роль кодов операций СIL

После того как сборка, пространство имен и набор типов .NET Core определены в терминах языка CIL с использованием различных директив и связанных атрибутов, остается только предоставить логику реализации для типов. Это работа кодов операций. В традициях других низкоуровневых языков программирования многие коды операций CIL обычно имеют непонятный и совершенно нечитабельный вид. Например, для загрузки в память переменной типа

string
применяется код операции, который вместо дружественного имени наподобие
LoadString
имеет имя
ldstr
.

Справедливости ради следует отметить, что некоторые коды операций CIL довольно естественно отображаются на свои аналоги в C# (например,

box
,
unbox
,
throw
и
sizeof
). Вы увидите, что коды операций CIL всегда используются внутри области реализации члена и в отличие от директив никогда не записываются с префиксом-точкой.

Разница между кодами операций и их мнемоническими эквивалентами в СIL

Как только что объяснялось, для реализации членов отдельно взятого типа применяются коды операций вроде

ldstr
. Однако такие лексемы, как
ldstr
, являются мнемоническими эквивалентами CIL фактических двоичных кодов операций CIL. Чтобы выяснить разницу, напишите следующий метод C# в проекте консольного приложения .NET Core по имени
FirstSamples
:

int Add(int x, int y)

{

Поделиться с друзьями: