image Приветствую. Недавно я наткнулся на статью товарища Dywar «Интересные заметки по C# и CLR» и заинтересовался пунктом 13:

«Константы помещаются в метаданные сборки, поэтому если были изменения, нужно перекомпилировать все использующие ее сборки. Т.к. 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» в явном виде.

ildasm

Смотрим описание инструкции 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 могло породить «баг неизменной константы».
Встречались ли вы с «багом неизменной константы»?

Проголосовало 249 человек. Воздержалось 94 человека.

Стоит ли расследовать детали работы nameof()?

Проголосовал 221 человек. Воздержалось 107 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Поделиться с друзьями
-->

Комментарии (18)


  1. MonkAlex
    05.09.2016 14:19
    +8

    По этой же причине не рекомендуется в апи делать методы со значениями по умолчанию — значение поменяете, а без пересборки зависимых сборок результата не получите.


    1. Oxoron
      05.09.2016 14:24

      А можно подробности? Имеется в виду дефолтное значение для автосвойств?


      1. lair
        05.09.2016 14:34
        +5

        Нет, имеется в виду void Method(bool critical = false). Если вызвать из другого проекта как Method(), затем поменять дефолтное значение, и подложить сборку без перекомпиляции зависимой сборки — будет старый дефолт.


      1. ColdPhoenix
        05.09.2016 14:34
        +2

        имеется в виду аргументы по умолчанию:

        void Test(string arg="arg")
        

        собственно потому многие дотнет классы имеют кучу перегрузок на методы, вместо параметров по умолчанию.


        1. iZevs
          06.09.2016 14:28

          Наверное всё-таки не поэтому, а из-за тяжелого наследия C# 3.0 и ранее, где значений по-умолчанию не было.
          В VB они были изначально и ничего.
          И я не припомню чтобы кто-то в здравом уме менял значения по-умолчанию в каких-либо библиотеках, это ж весь софт который их использует, полетит нафиг. И уже будет не важно где они там хранятся.


          1. MonkAlex
            06.09.2016 14:45

            МС — не меняет, остальные — могут себе позволить, кто ещё настолько популярен то в плане библиотек?


    1. alexeyrom
      07.09.2016 19:46
      +1

      А вот Scala здесь умная: вычисление значения по умолчанию компилируется в метод, так что при изменении значения по умолчанию поменяется код вызванного метода, а на вызывающей стороне изменений никаких нет и не должно быть.


  1. Oxoron
    05.09.2016 14:24

    del


  1. EimKR
    05.09.2016 15:11

    Была схожая проблема но с enum:
    Есть enum который содержит список операций. За этот enam, а также и за библиотеку (DLL), его использующую отвечает разработчик А. Этот enam входит в другую библиотеку.
    Разработчик Б использует эту библиотеку (DLL).
    Разработчик А допустил ошибку в enum и прислал новую версию библиотеки (DLL) разработчику Б.
    У разработчика Б проявлялся странный баг, было установлено, что причина в этом enam (использовался рефлектор).
    Заменяется сборка с enam, где ошибка исправлена — баг остается.
    Рефлектор показывает, что в библиотеке (DLL) разработчика А используется старое значение (в виде byte).
    Только когда библиотека (DLL) была пересобрана с исправленной версией enam проблема была решена.


    1. ColdPhoenix
      05.09.2016 16:23

      оно логично, enum по сути набор констант.


    1. PahanMenski
      05.09.2016 18:19

      У нас была анологичная проблема с enum при переходе c WiX 3.9 на 3.10


      1. sergeyivashkevich1
        06.09.2016 07:27

        Столкнувшись с похожей проблемой с enum стали использовать следующий подход: Enumeration classes.


  1. impwx
    05.09.2016 15:14
    +19

    Далее в программе «Удивительное и невероятное из документации по .NET»: структуры передаются по значению, приведение примитива к object вызывает боксинг, строки являются неизменяемыми…


    1. Oxoron
      05.09.2016 15:53

      Вы правы: зевнул док при написании статьи.


  1. horse315
    05.09.2016 16:24
    +2

    Неоднократно встречал эту информацию в разных книжках прямо в определении того, что такое константа в clr и в голову не приходило ставить его под сомнение. Когда появились параметры со значениями по умолчанию, то принцип их работы и поведение так же были описаны. Мне кажется этот факт не стоит выносить дальше помещения, где коллеги-разработчики обсуждают новые фишечки языка. Копипастер статью скорее всего не встретит, он обитает на stackexchange, где и скопирует кусок кода про значения по умолчанию.


  1. yan4esko
    06.09.2016 09:50
    +2

    Это все-таки не бага, а фича. У Рихтера можно почитать, если не ошибаюсь.

    А по поводу nameof — все то же самое, вычисляется в compile-time и записывается как константа. Недавно проверял.


    1. selitskas
      06.09.2016 14:51

      Да, цитирую Рихтера (перевод, 4-е издание, ISBN 978-5-496-00433-6): «Если разработчик изменит значение константы… и перестроит DLL-сборку, это не повлияет на код самого приложения. Для того чтобы в приложении использовалось новое значение константы, его тоже необходимо перекомпилировать». [стр. 211, последний абзац]


  1. user004
    07.09.2016 17:55
    +1

    Использую по этой причине static readonly почти всегда.