«Константы помещаются в метаданные сборки, поэтому если были изменения, нужно перекомпилировать все использующие ее сборки. Т.к. DLL с константой может даже не загружаться.»
Да ладно, подумал я, и полез ставить эксперименты. Создал проект TestConstants, в нем класс,
public class Master
{
public const string Const = "Hello world";
}
затем проект TestConstantsSlave,
static void Main()
{
Console.WriteLine(Master.Const);
Console.ReadKey();
}
Заменил константу на “Hello habr”, перебилдил основной проект, запустил по F5…
На консоли высветился “Hello habr”. Видимо, студия ребилднула заодно и Slave проект. В принципе, на этом можно было успокоиться, но хотелось немного поэкспериментировать.
Первым делом я заменил константу на “Greetings, sir von Neumann”, ребилднул основной проект, и запустил Slave через проводник > TestConstantsSlave.exe. На консоли светилось «Hello habr», да простит меня великий Нейман. Скопировал свежую библиотеку к Slave – по прежнему «Hello habr”. Удалил TestConstants библиотеку — снова “Hello habr”.
Неужели действительно константа копируется в метаданные? Открываем ildasm. В манифесте Slave все стандартно: версия, заголовки, токены. Константы нет (что разумно). Пробежал по классам — никаких метаданных в явном виде не обнаружено.
Зато в коде Slave есть «Hello habr» в явном виде.
Смотрим описание инструкции ldstr на msdn. “Pushes a new object reference to a string literal stored in the metadata“. Видимо, метаданные скрыты для ildasm. На всякий случай проверил dotPeek – результат аналогичен (зато видно, что Slave проект не ссылается на TestConstants).
Проверка константы другого типа: использование Int32 константы дает другой il код: ldc.i4.5. Здесь у нас простой пуш инта в стек (что опять-таки разумно — строки длинные, есть смысл их кэшировать, а вот накладные расходы на вытягивание инта из кэша перевешивают выхлоп). Видимо, в изначальной статье и msdn под метаданными подразумевается часть механизма интернирования строк (тунц, тынц), что, кстати, объясняет отсутствие метаданных в ildasm — пул строк генерируется для процесса.
И что дальше?
Ничего страшного. Студия же правильно билдит, да и константы меняются не каждый день. С другой стороны, я уже работал с проектом, где SDK и использующие его утилиты находились в разных солюшнах, и обновление SDK могло породить «баг неизменной константы».
Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Комментарии (18)
EimKR
05.09.2016 15:11Была схожая проблема но с enum:
Есть enum который содержит список операций. За этот enam, а также и за библиотеку (DLL), его использующую отвечает разработчик А. Этот enam входит в другую библиотеку.
Разработчик Б использует эту библиотеку (DLL).
Разработчик А допустил ошибку в enum и прислал новую версию библиотеки (DLL) разработчику Б.
У разработчика Б проявлялся странный баг, было установлено, что причина в этом enam (использовался рефлектор).
Заменяется сборка с enam, где ошибка исправлена — баг остается.
Рефлектор показывает, что в библиотеке (DLL) разработчика А используется старое значение (в виде byte).
Только когда библиотека (DLL) была пересобрана с исправленной версией enam проблема была решена.PahanMenski
05.09.2016 18:19У нас была анологичная проблема с enum при переходе c WiX 3.9 на 3.10
sergeyivashkevich1
06.09.2016 07:27Столкнувшись с похожей проблемой с enum стали использовать следующий подход: Enumeration classes.
horse315
05.09.2016 16:24+2Неоднократно встречал эту информацию в разных книжках прямо в определении того, что такое константа в clr и в голову не приходило ставить его под сомнение. Когда появились параметры со значениями по умолчанию, то принцип их работы и поведение так же были описаны. Мне кажется этот факт не стоит выносить дальше помещения, где коллеги-разработчики обсуждают новые фишечки языка. Копипастер статью скорее всего не встретит, он обитает на stackexchange, где и скопирует кусок кода про значения по умолчанию.
yan4esko
06.09.2016 09:50+2Это все-таки не бага, а фича. У Рихтера можно почитать, если не ошибаюсь.
А по поводу nameof — все то же самое, вычисляется в compile-time и записывается как константа. Недавно проверял.selitskas
06.09.2016 14:51Да, цитирую Рихтера (перевод, 4-е издание, ISBN 978-5-496-00433-6): «Если разработчик изменит значение константы… и перестроит DLL-сборку, это не повлияет на код самого приложения. Для того чтобы в приложении использовалось новое значение константы, его тоже необходимо перекомпилировать». [стр. 211, последний абзац]
MonkAlex
По этой же причине не рекомендуется в апи делать методы со значениями по умолчанию — значение поменяете, а без пересборки зависимых сборок результата не получите.
Oxoron
А можно подробности? Имеется в виду дефолтное значение для автосвойств?
lair
Нет, имеется в виду
void Method(bool critical = false)
. Если вызвать из другого проекта какMethod()
, затем поменять дефолтное значение, и подложить сборку без перекомпиляции зависимой сборки — будет старый дефолт.ColdPhoenix
имеется в виду аргументы по умолчанию:
собственно потому многие дотнет классы имеют кучу перегрузок на методы, вместо параметров по умолчанию.
iZevs
Наверное всё-таки не поэтому, а из-за тяжелого наследия C# 3.0 и ранее, где значений по-умолчанию не было.
В VB они были изначально и ничего.
И я не припомню чтобы кто-то в здравом уме менял значения по-умолчанию в каких-либо библиотеках, это ж весь софт который их использует, полетит нафиг. И уже будет не важно где они там хранятся.
MonkAlex
МС — не меняет, остальные — могут себе позволить, кто ещё настолько популярен то в плане библиотек?
alexeyrom
А вот Scala здесь умная: вычисление значения по умолчанию компилируется в метод, так что при изменении значения по умолчанию поменяется код вызванного метода, а на вызывающей стороне изменений никаких нет и не должно быть.