Когда предоставленный пример встроен в режим выпуска, а затем JIT-преобразован в 64-битный машинный код, он не содержит достаточно информации для отладчика, чтобы сопоставить точку останова с какой-либо конкретной машинной инструкцией. Вот почему отладчик никогда не останавливается на этой точке останова во время выполнения машинного кода в формате JIT. Он просто не знает, где остановиться. Возможно, это какое-то неправильное поведение или даже ошибка в 64-битном отладчике CLR, потому что он воспроизводим только тогда, когда он JIT-вписан в 64-битный машинный код, но не в 32-битный машинный код.
Когда отладчик видит точку останова в вашем коде, он пытается найти машинную инструкцию в коде JIT, которая соответствует местоположению, помеченному точкой останова. Во-первых, ему нужно найти инструкцию IL, соответствующую местоположению точки останова в вашем коде C #. Затем необходимо найти машинную инструкцию, соответствующую команде IL. Затем он устанавливает реальную точку останова на найденной машинной инструкции и начинает выполнение метода. В вашем случае, похоже, что отладчик просто игнорирует точку останова, потому что он не может сопоставить ее с конкретной машинной инструкцией.
Отладчик не может найти адрес машинной инструкции, которая следует непосредственно за оператором if… else. Оператор if… else и код внутри него так или иначе вызывают такое поведение. Неважно, какое утверждение следует за if ... else. Вы можете заменить оператор Console.WriteLine («2») другим, и вы все равно сможете воспроизвести проблему.
Вы увидите, что компилятор C # испускает блок try… catch вокруг логики, которая читает список, если вы разберете получившуюся сборку с помощью Reflector. Это документированная особенность компилятора C #. Вы можете прочитать больше об этом в Заявление foreach
Блок try… catch… finally имеет довольно инвазивный эффект для кода JIT. Он использует механизм Windows SEH и плохо переписывает ваш код. Я не могу найти ссылку на хорошую статью прямо сейчас, но я уверен, что вы можете найти ее там, если вам интересно.
Это то, что здесь происходит. Блок try… finally внутри оператора if… else вызывает сбой отладчика. Вы можете воспроизвести проблему с помощью простого кода.
bool b = false;
if (b)
{
try
{
b = true;
}
finally
{
b = true;
}
}
else
{
b = true;
}
b = true;
Этот код не вызывает никаких внешних функций (он исключает эффект встраивания метода, предложенного одним из ответов) и компилируется непосредственно в IL без каких-либо дополнительных кодов, добавляемых компилятором C #.
Воспроизводится только в режиме выпуска, потому что в режиме отладки компилятор выдает инструкцию IL NOP для каждой строки вашего кода C #. Инструкция IL NOP ничего не делает, и она напрямую компилируется в инструкцию CPU NOP от JITer, который тоже ничего не делает. Полезность этой инструкции заключается в том, что она может использоваться отладчиком в качестве якоря для точек останова, даже если остальная часть кода плохо переписана JITer.
Мне удалось заставить отладчик работать правильно, поместив одну инструкцию NOP прямо перед оператором, который следует за if ... else.
Подробнее об операциях NOP и отображении отладчика вы можете прочитать здесь Отладка IL
Вы можете попробовать использовать WinDbg и расширение SOS, чтобы проверить JIT-версию метода. Вы можете попытаться изучить машинный код, который генерирует JIT-er, и попытаться понять, почему он не может сопоставить этот машинный код с определенной строкой C #.
Вот пара ссылок об использовании WinDbg для взлома управляемого кода и получения адреса памяти метода JIT. Я считаю, что вы должны быть в состоянии найти способ получить код в формате JIT для метода оттуда: Установка точки останова в WinDbg для управляемого кода , Шпаргалка SOS (.NET 2.0 / 3.0 /3.5).
Вы также можете попытаться сообщить о проблеме в Microsoft. Вероятно, это ошибка отладчика CLR.
Спасибо за интересный вопрос.