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

ЖАНРЫ

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

Определение конструкторов типа в CIL

Спецификация CTS поддерживает создание конструкторов как уровня экземпляра, так и уровня класса (статических). В CIL конструкторы уровня экземпляра представляются с использованием лексемы

.ctor
, тогда как конструкторы уровня класса — посредством лексемы
.cctor
(class constructor — конструктор класса). Обе лексемы CIL должны сопровождаться атрибутами
rtspecialname
(return type special name — специальное имя возвращаемого типа) и
specialname
. Упомянутые атрибуты применяются для обозначения специфической лексемы CIL, которая может трактоваться уникальным образом в любом отдельно взятом языке .NET Core. Например, в языке C# конструкторы не определяют возвращаемый тип,
но в CIL возвращаемым значением конструктора на самом деле является
void
:

.class public MyBaseClass

{

.field private string stringField

.field private int32 intField

.method public hidebysig specialname rtspecialname

instance void .ctor(string s, int32 i) cil managed

{

// Добавить код реализации...

}

}

Обратите внимание, что директива

.ctor
снабжена атрибутом
instance
(поскольку конструктор не статический). Атрибуты
cil managed
указывают на то, что внутри данного метода содержится код CIL, а не неуправляемый код, который может использоваться при выполнении запросов Р/Invoke.

Определение свойств в CIL

Свойства и методы также имеют специфические представления в CIL. В качестве примера модифицируйте класс

MyBaseClass
с целью поддержки открытого свойства по имени
TheString
, написав следующий код CIL (обратите внимание на применение атрибута
specialname
):

.class public MyBaseClass

{

...

.method public hidebysig specialname

instance string get_TheString cil managed

{

// Добавить код реализации...

}

.method public hidebysig specialname

instance void set_TheString(string 'value') cil managed

{

// Добавить код реализации...

}

.property instance string TheString

{

.get instance string

MyNamespace.MyBaseClass::get_TheString

.set instance void

MyNamespace.MyBaseClass::set_TheString(string)

}

}

В терминах CIL свойство отображается на пару методов, имеющих префиксы

get_
и
set_
. В директиве
.property
используются связанные директивы
.get
и
.set
для отображения синтаксиса свойств на подходящие "специально именованные" методы.

На заметку! Обратите внимание, что входной параметр метода

set
в свойстве помещен в одинарные кавычки и представляет имя лексемы, которая должна применяться в правой части операции присваивания внутри области определения метода.

Определение параметров членов

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

по ссылке параметры. Вдобавок в CIL допускается определять массив параметров (соответствует ключевому слову
params
в С#), а также необязательные параметры.

Чтобы проиллюстрировать процесс определения параметров в низкоуровневом коде CIL, предположим, что необходимо построить метод, который принимает параметр

int32
(по значению), параметр
int32
(по ссылке), параметр
[System.Runtime.Extensions]System.Collection.ArrayList
и один выходной параметр (типа
int32
). В C# метод выглядел бы примерно так:

public static void MyMethod(int inputInt,

ref int refInt, ArrayList ar, out int outputInt)

{

outputInt = 0; // Просто чтобы удовлетворить компилятор C#...

}

После отображения метода

MyMethod
на код CIL вы обнаружите, что ссылочные параметры C# помечаются символом амперсанда (
&
), который дополняет лежащий в основе тип данных (
int32 &
).

Выходные параметры также снабжаются суффиксом

&
, но дополнительно уточняются лексемой
[out]
языка CIL. Вдобавок если параметр относится к ссылочному типу (
[System.RuntimeExtensions]System.Collections.ArrayList
), то перед типом данных указывается лексема
class
(не путайте ее с директивой
.class
):

.method public hidebysig static void MyMethod(int32 inputInt,

int32& refInt,

class [System.Runtime.Extensions]System.Collections.ArrayList ar,

[out] int32& outputInt) cil managed

{

...

}

Исследование кодов операций CIL

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

Все коды операций CIL (которых довольно много) могут быть разделены на три обширные категории:

• коды операций, которые управляют потоком выполнения программы ;

• коды операций, которые вычисляют выражения;

• коды операций, которые получают доступ к значениям в памяти (через параметры, локальные переменные и т.д.).

В табл. 19.4 описаны наиболее полезные коды операций, имеющие прямое отношение к логике реализации членов; они сгруппированы по функциональности.

Коды операций из следующей обширной категории (подмножество которых описано в табл. 19.5) применяются для загрузки (заталкивания) аргументов в виртуальный стек выполнения. Обратите внимание, что все эти ориентированные на загрузку коды операций имеют префикс

Id
(load — загрузить).

В дополнение к набору кодов операций, связанных с загрузкой, CIL предоставляет многочисленные коды операций, которые явно извлекают из стека самое верхнее значение. Как было показано в нескольких начальных примерах, извлечение значения из стека обычно предусматривает его сохранение во временном локальном хранилище с целью дальнейшего использования (наподобие параметра для предстоящего вызова метода). Многие коды операций, извлекающие текущее значение из виртуального стека выполнения, снабжены префиксом

st
(store — сохранить). В табл. 19.6 описаны некоторые распространенные коды операций.

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