Определение метода. Точка входа в программу

Последнее обновление: 24.07.2022

Для определения действий в программе применяются методы. Метод определяется с помощью директивы .method

.method атрибуты тип название (параметры) [атрибуты_реализации]
{
   // действия метода
}

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

Например, возьмем простейший пример:

.assembly extern System.Runtime { }
.assembly extern System.Console { }
.assembly HelloApp{}
.module HelloApp.dll
.class Program
{
  .method static void main()
  {
    .entrypoint
    ldstr      "Hello, METANIT.COM!"
    call       void [System.Console]System.Console::WriteLine(string)
    ret
  }
}

Здесь метод называется main. Этот метод имеет один атрибут - static, который указывает, что метод - статический.

В качестве возвращаемого типа метод использует ключевое слово void, которое указывает, что метод ничего не возвращает, просто выполняет некоторые действия.

Метод называется main. После названия идут пустые круглые скобки, что значит, что метод не принимает никаких параметров.

Стоит отметить, что метод необязательно должен быть определен в классе: он также может быть определен на уровне модуля (так называемый глобальный метод):

.assembly extern System.Runtime { }
.assembly extern System.Console { }
.assembly HelloApp{}
.module HelloApp.dll
.method static void main()
{
  .entrypoint
  ldstr      "Hello, METANIT.COM!"
  call       void [System.Console]System.Console::WriteLine(string)
  ret
}

Атрибуты реализации

Между списком параметров метода и его действия в фигурных скобках могут идти атрибуты реализации метода:

  • cil: указывает, что метод содержит код Common Intermediate Language (то есть MSIL)

  • native: указывает, что метод использует нативный код, который предназначен для определенной архитектуры. Обычно это методы, которые используют вызовы PInvoke

  • runtime: указывает, что реализация метода предоставляется средой выполнения, обычно применяется для делегатов

  • managed: указывает, что метод содержит управляемый код .net. Применяется в связке с атрибутом cil

  • unmanaged: указывает, что метод содержит код неуправляемый код, например, вызовы PInvoke. Применяется только в связке с атрибутом native

  • forwardref: указывает, что тело метода определено в другом месте

  • internalcall: указывает, что реализация метода предоставляется CLI (Common Language Infrastructure) и обычно применяется для низкоуровневых вызовов в системной библиотеке

  • noinlining: указывает, что среда выполнения не будет применять инлайнинг - замена вызова метода непосредственным кодом этого метода

  • nooptimization: указывает, что компилятор из кода MSIL в нативный код не должен выполнять оптимизации.

  • synchronized: компилятор автоматически будет создавать для доступа к методу объект-заглушку (lock). Это значит, что второй вызов метода может начать выполняться только после того, как первый вызов метода освободит объект-заглушку (а это происходит после завершения метода)

Пример использования подобных флагов:

.method static void main() cil managed
{
  .entrypoint
  ldstr      "Hello METANIT.COM"
  call       void [System.Console]System.Console::WriteLine(string)
  ret
}

Стоит отметить, что в данном случае использовать атрибуты cil managed необязательно, так как они добавляются по умолчанию.

Точка входа в программу

Если речь идет о исполняемой программе, а не о библиотеке классов, то она содержит метод, который представляет точку входа в программу. И с этого метода собственно и начинается выполнение программы. А этот метод может запускать все остальные методы. Так, в программе выше такой точкой входа служит единственный метод main. При этом название метода не играет роли. По традиции он называется "main".

Но главным компонентом метода-точки входа служит директива .entrypoint, которая помещается перед остальным содержимым метода. Кроме того, подобный метод должен быть определен как статический. Поэтому метод main использует атрибут static.

.method static void main()
{
  .entrypoint
  // содержимое метода
}

При обращении к этой программе среда будет запускать метод с директивой .entrypoint. При этом сборка должна содержать только один метод с .entrypoint.

Несколько ограничений по определению точки входа:

  • Метод в качестве параметра может принимать только массив строк. В качестве альтернативы он может не принимать вообще никаких параметров, как в примере выше.

  • Возвращаемым типом метода могут быть только void, int32 или unsigned int32. Если используются последние два типа (то есть если возвращается целое число), то возвращение числа 0 сигнализирует системе об успешном выполнении.

ret. Выход из метода

Для выхода из метода применяется инструкция ret. Соответственно в конце метода main выполняется инструкция ret. Если ret не использовать, то при выполнении программы мы столкнемся с ошибкой.

Вызов метода

Для вызова метода применяется инструкция call.

call [method] [конвенции_вызова] возвращаемый_тип [имя_сборки]имя_класса::имя_метода(аргументы_метода)

Так, в примере выше как происходит вызов метода для вывода строки на консоль:

ldstr      "Hello, METANIT.COM!"
call       void [System.Console]System.Console::WriteLine(string)

инструкция ldstr помещает в стек строку "Hello, METANIT.COM!". Затем инструкция call вызывает метод WriteLine, который определен в классе System.Console и располагается в одноименной сборке "System.Console". Метод WriteLine имеет сигнатуру void(string), то есть его возвращаемым типом является void, а в качестве параметра он представляет строку - значение типа string. Стоит обратить внимание, что в метод при вызове передается название типа параметра, а само значение берется из верхушки стека. В данном случае в метод будет передаваться строка из стека.

Рассмотрим пример, когда вызывается метод из текущего модуля:

.assembly extern System.Runtime { }
.assembly extern System.Console { }
.assembly HelloApp{}
.module HelloApp.dll
.class Program
{
  .method static void main()
  {
    .entrypoint
    call void Program::PrintHello()
    ret
  }
  .method static void PrintHello()
  {
    ldstr      "Hello"
    call       void [System.Console]System.Console::WriteLine(string)
    ret
  }
}

В данном случае определен второй статический метод PrintHello, который также выводит строку на консоль.

В методе main вызывается метод PrintHello. Данный метод имеет тип void. Он расположен в классе Program, поэтому указывается Program::PrintHello. Он не принимает параметров, поэтому при вызове указываются пустые скобки.

call void Program::PrintHello()

Если вызываемый метод определен на уровне модуля (то есть является глобальным), то имя класса при вызове не используется:

.assembly extern System.Runtime { }
.assembly extern System.Console { }
.assembly HelloApp{}
.module HelloApp.dll
.method static void main()
  {
    .entrypoint
    call void PrintHello()
    ret
  }
  .method static void PrintHello()
  {
    ldstr      "Hello"
    call       void [System.Console]System.Console::WriteLine(string)
    ret
  }
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850