Требуется ли инструкция ret для приложений .NET?
Я заметил, что компилятор С# генерирует команду ret
в конце методов void
:
.method private hidebysig static void Main(string[] args) cil managed
{
// method body
L_0030: ret
}
Я написал компилятор для .NET, и он работает независимо от того, испускал ли я оператор ret
или нет (я проверил сгенерированный IL, и он действительно не там).
Я просто задаюсь вопросом: требуется ли ret
для методов, возвращающих void
для чего-либо? Кажется, он ничего не делает со стек, поэтому я считаю, что это совершенно не нужно для методов void
, но я хотел бы услышать от кого-то, кто знает немного больше о CLR?
Ответы
Ответ 1
Согласно стандарту С# (ECMA-334), метод определяется следующим образом:
Метод - это элемент, который реализует вычисление или действие, которое может быть выполнено объектом или классом. Методы имеют (возможно, пустой) список формальных параметров, возвращаемое значение (если только методы return-type не являются void) и являются либо статическими, либо нестатическими.
(ECMA-334; 8.7.3: Методы).
Теперь стандарт CLI определяет следующее:
Управлению не разрешается просто "проваливать" конец метода. Все пути должны заканчиваться с одной из следующих инструкций: ret, throw, jmp или (tail, затем вызов, calli или callvirt).
(ECMA-335; 12.4, 6)
Это означает, что в С# метод, возвращающий void
, не нуждается в операторе return. Однако, поскольку компилятор С# компилирует код С# в IL-код, который требует завершения пути в конце метода, он испускает ret
для завершения метода.
Ответ 2
Это действительно необходимо для проверки кода. В противном случае PEVerify выведет следующее сообщение об ошибке:
[IL]: Ошибка: [(filename): (methodname)] [offset 0x00000000] проваливается в конец метода без возврата
Ответ 3
Из Ecma-335. (12.4, 6)
Управлению не разрешается просто "проваливать" конец метода. Все пути должны заканчиваться с одной из следующих инструкций: ret, throw, jmp или (tail, затем вызов, calli или callvirt).