Почему компилятор создает инструкцию, которая ничего не делает при возврате строки из метода?

Я смотрел на 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