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