Почему компилятор создает инструкцию, которая ничего не делает при возврате строки из метода?
Я смотрел на IL, сгенерированный для очень простого метода, потому что я хочу сделать небольшое отражение отражения, и я наткнулся на то, что упоминается в комментариях в этом вопросе (но это не вопрос): Используя Br_S OpCode, чтобы указать на следующую команду, используя Reflection.Emit.Label, и никто не ответил на это, и мне интересно об этом. так что...
Если у меня есть такой метод:
public string Test()
{
return "hello";
}
а затем я запускаю ILDASM. Я вижу, что IL:
.method public hidebysig instance string
Test() cil managed
{
// Code size 11 (0xb)
.maxstack 1
.locals init ([0] string CS$1$0000)
IL_0000: nop
IL_0001: ldstr "hello"
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000a: ret
}
Часть, которую я нахожу любопытной:
IL_0007: br.s IL_0009
IL_0009: ldloc.0
Первая строка делает Unconditional Transfer во вторую строку. В чем причина этой операции, разве она ничего не делает?
ИЗМЕНИТЬ
Кажется, мой вопрос был сформулирован плохо, поскольку есть некоторая путаница в отношении того, что я хотел знать. Последнее предложение должно быть примерно таким:
В чем причина того, что компилятор выводит этот оператор безусловной передачи, когда он, кажется, не служит цели?
UPDATE
Предложение о том, что это было для точки останова, заставило меня подумать попробовать и скомпилировать это в режиме Release, и, конечно же, часть, которую меня интересует, исчезла, и IL стал именно этим (вот почему я прыгнул с пистолета и думал, что ответ на точку останова был причиной):
.method public hidebysig instance string
Test() cil managed
{
// Code size 6 (0x6)
.maxstack 8
IL_0000: ldstr "hello"
IL_0005: ret
}
Вопрос о том, "почему он там", по-прежнему играет на мой взгляд - если это не так, как всегда работает компилятор, и это не для какой-то полезной причины отладки (например, когда-то место точки останова), почему это на всех?
Я думаю, что ответ, вероятно, "просто так, как он был сделан, нет веских оснований, и это не имеет большого значения, потому что JIT в конце концов все будет хорошо разбираться".
Мне жаль, что я не спросил об этом сейчас, это испортит мой процент принятия!: -)
Ответы
Ответ 1
Первая из двух команд является частью стандартного кода для оператора return
, вторая команда является частью шаблона кода для метода.
Оператор return
помещает возвращаемое значение в локальную переменную, затем он переходит к точке выхода метода:
IL_0001: ldstr "hello"
IL_0006: stloc.0
IL_0007: br.s IL_0009
Код шаблона метода получает возвращаемое значение из локальной переменной и затем выходит из метода:
IL_0009: ldloc.0
IL_000a: ret
В коде IL, создаваемом компилятором, метод всегда имеет единственную точку выхода. Вот почему оператор return переходит к этому местоположению, а не просто выходит из функции напрямую. Код для оператора return всегда один и тот же, поэтому всегда существует ветвь, даже если она переходит к следующей инструкции.
Компилятор часто создает код IL, который выглядит неэффективным, поскольку компилятор JIT оптимизирует код. Компилятор создает неоптимизированный, простой и предсказуемый код, который проще для компилятора JIT для оптимизации.
Ответ 2
Одна из причин, по которой команда NOP находится в debug, создает ее, чтобы вы могли установить точки останова.
Внутренние VB.net, перейдите к отладке
Visual Basic.NET позволяет устанавливать контрольные точки на неиспользуемые строки кода, такие как End If, End Sub и Dim. Чтобы облегчить эту популярную технику отладки, компилятор вставляет nop-команды в качестве заполнителей для неисполнимых строк кода (поскольку неисполняющие строки не переводятся в инструкции IL). Команда nop является инструкцией "без операции" - она не выполняет никакой значимой работы, но может потреблять цикл обработки.
Ответ 3
Быстрый просмотр в Интернете sugests, что это может быть код для оператора return. Он должен был вернуть код в конец функции, и он должен работать из любого места в теле функции. Я предполагаю, что он может быть оптимизирован, но вы не указали уровень оптимизации вашего кода.
Ответ 4
На промежуточном языке, когда вы видите инструкцию "br", это указывает ветвь. Инструкция ветки - условная инструкция что изменит, какая инструкция будет выполняться следующим образом на основе состояние. На языке С# такие вещи, как goto, while, for, break, и return могут быть реализованы с вариантами ветки инструкция.
http://www.dotnetperls.com/il