Почему я не могу редактировать метод, содержащий анонимный метод в отладчике?
Итак, каждый раз, когда я писал выражение лямбда или анонимный метод внутри метода, который мне не совсем понятен, мне приходится перекомпилировать и перезагрузить все приложение или структуру unit test, чтобы исправить это. Это очень раздражает, и я в конечном итоге трачу больше времени, чем я спас, используя эти конструкции в первую очередь. Это так плохо, что я стараюсь держаться подальше от них, если могу, хотя Linq и lambdas относятся к моим любимым функциям С#.
Я полагаю, что есть хорошая техническая причина, почему это так, и, возможно, кто-то знает? Кроме того, кто-нибудь знает, будет ли он исправлен в VS2010?
Спасибо.
Ответы
Ответ 1
Да, есть очень веская причина, почему вы не можете этого сделать. Простая причина - это стоимость. Стоимость включения этой функции в С# (или VB) высока чрезвычайно.
Редактирование лямбда-функции - это конкретный случай класса вопросов ENC, которые очень трудно решить с текущей архитектурой ENC (Edit'n'Continue). А именно, очень сложно ENC любой метод, в котором ENC выполняет одно из следующих действий: -
- Генерирует метаданные в виде класса
- Редактирует или генерирует общий метод
Первая проблема связана скорее с логическим ограничением, но она также сталкивается с несколькими ограничениями в архитектуре ENC. А именно проблема создания первого класса не является ужасно трудной. То, что надоедливое, генерирует класс после второго редактирования. Двигатель ENC должен начать отслеживать таблицу символов не только для живого кода, но и для сгенерированных классов. Обычно это не так уж плохо, но это становится все труднее, когда форма сгенерированного класса основана на контексте, в котором он используется (как в случае с lambdas из-за закрытия). Что еще более важно, как вы решаете различия между экземплярами классов, которые уже живы в этом процессе?
Вторая проблема - строгое ограничение в архитектуре ENC CLR. Нет ничего, что С# (или VB) может сделать, чтобы обойти это.
Лямбдас, к сожалению, ударил по обоим этим вопросам. Короткий вариант заключается в том, что ENC'ing лямбда включает в себя множество мутаций на существующих классах (которые могут быть или не быть созданы из других ENC). Большая проблема заключается в устранении различий между новым кодом и существующими экземплярами закрытия, живущими в текущем пространстве процесса. Кроме того, лямбды, как правило, используют дженерики намного больше, чем другие коды, и попадают в проблему №2.
Детали довольно волосатые и немного слишком заняты для нормального ответа SO. Я подумывал написать длинный пост в блоге по этому вопросу. Если я подберусь к нему, я свяжу его с этим конкретным ответом.
Ответ 2
В соответствии со списком Поддерживаемые изменения кода вы не можете добавлять поля к существующим типам. Анонимные методы скомпилированы в классы с нечетным именем (kinda <>_c__DisplayClass1
), которые являются именно такими: types. Несмотря на то, что ваши модификации анонимного метода могут не включать в себя изменение набора закрытых переменных (добавление этих параметров будет изменять поля существующего класса), я предполагаю, что причина невозможности изменения анонимных методов.
Ответ 3
Немного стыдно, что эта функция частично поддерживается в VB, но не в С#:
http://msdn.microsoft.com/en-us/library/bb385795.aspx
Реализация такого же поведения в С# приведет к уменьшению уровня боли на 80% для функций, содержащих лямбда-выражения, где нам не нужно изменять лямбда-выражения или любое выражение, которое зависит от них, и, вероятно, не для "стоимости монстра" ".
Ответ 4
Перезапуск unit test должен занять несколько секунд. Мне никогда не нравилась модель "изменить и продолжить", если честно - вы всегда должны повторять с нуля ИМО, на всякий случай, если изменение в середине исполнения повлияло бы на код, который был раньше. Учитывая, что вам лучше использовать модульные тесты, которые можно запускать с очень быстрым поворотом. Если ваши индивидуальные тесты требуют невыносимого времени для запуска, это то, что вы должны посмотреть на адресацию.
EDIT: Что касается того, почему это не работает, вы можете обнаружить, что оно работает для некоторых лямбдов, но не для других. Лямбда-выражения, которые не захватывают никакие переменные (включая this
), кэшируются в частной статической переменной, так что создается только один экземпляр делегата. Изменение кода означает повторную инициализацию этой переменной, которая может иметь интересные побочные эффекты, которые я подозреваю.