Когда создавать и распространять "справочные сборки"?
C# 7.1 ввел несколько новых параметров командной строки, чтобы помочь создать "эталонные сборки". По документации он выводит сборку, которая:
замените тела их методов единичным нулевым телом, но включите все члены, кроме анонимных типов.
Я нашел интересное замечание, что оно более стабильно при изменениях:
Это означает, что он меняется реже, чем полная сборка - многие обычные действия по разработке не меняют интерфейс, а только реализацию. Это означает, что инкрементные сборки могут быть намного быстрее...
и что это наверное нужно для самого рослина..
Мы будем вводить вторую концепцию, которая называется "опорные сборки" (также называемые каркасными сборками). [---] Они будут использоваться для сценариев сборки.
.. какими бы ни были эти "сценарии сборки" для Roslyn.
Я понимаю, что для обычных пользователей сборки.NET такая сборка, вероятно, меньше и немного быстрее загружается для отражения. Да, но:
- обычно вы также заботитесь о выполнении, и сборка реализации уже содержит все данные из ссылочной сборки,
- довольно часто вы не заботитесь об этой незначительной разнице в производительности при загрузке,
- и самое главное - обычно у вас нет этой урезанной эталонной сборки (распространяемой) вообще.
Это полезность кажется довольно нишевым.
Итак, я задаюсь вопросом о стороне производителя общих сборок - когда следует явно использовать эти новые флаги компилятора для создания эталонной сборки? Есть ли у него какое-либо практическое применение вне самого Roslyn?
1 ответ
Мотивация для этой функции - это действительно сценарии сборки, но они не специфичны для Roslyn; это тоже ваши сценарии сборки.
Когда вы собираете свой проект, движок сборки (MSBuild) должен решить, является ли каждый выход сборки актуальным по отношению к его входам. Например, если вы ничего не меняете и просто запускаете сборку дважды подряд, во второй раз не нужно вызывать компилятор C#: сборка уже была правильной.
Справочные сборки позволяют пропустить этап компиляции для сборок в большем количестве сценариев, чтобы ваши сборки могли быть быстрее. Я думаю, что пример поможет проиллюстрировать.
Предположим, у вас есть решение, содержащее B.exe
это зависит от A.dll
,
Командная строка компилятора для B будет выглядеть примерно так
csc.exe /out:B.exe /r:..\A\bin\A.dll Program.cs
И его вклад будет
- Источник для Б (
Program.cs
) - Сборка для А.
Если вы измените источник A и создадите свое решение, компилятор должен работать для A, создавая новый A.dll
, Тогда, так как A.dll
является входом для компиляции B, B также должен быть перекомпилирован.
Использование эталонной сборки для A немного меняет это
csc.exe /out:B.exe /r:..\A\bin\ref\A.dll Program.cs
Ввод для A теперь является его эталонной сборкой, а не реализацией / нормальной сборкой.
Поскольку эталонная сборка меньше полной сборки, это само по себе незначительно влияет на время сборки. Но этого недостаточно, чтобы оправдать эту особенность. Важно то, что компилятор заботится только о публичной поверхности API переданных ссылок. Если внутренняя деталь реализации сборки изменилась, сборки, на которые она ссылается, не нужно перекомпилировать для получения нового поведения. Как упоминает @Hans Passant в комментариях, именно так.NET Framework может обеспечить совместимые улучшения производительности и исправления ошибок в неизмененном пользовательском коде.
Преимущество функции эталонных сборок связано с работой MSBuild по их использованию. Предположим, вы изменили внутреннюю деталь реализации в A, но не изменили ее открытый интерфейс. На следующей сборке
- Компилятор должен работать для A, потому что исходные файлы для A изменены.
- Компилятор испускает оба
A.dll
(с измененной реализацией) иref\A.dll
, что идентично предыдущей справочной сборке. - поскольку
ref\A.dll
идентичен предыдущему выводу, он не копируется в выходную папку А. - Когда приходит время запустить компилятор B, он видит, что ни один из его входных данных не изменился - ни собственный код B, ни ссылочная сборка A, поэтому компилятору не нужно запускаться.
- B затем копирует обновленный
A.dll
на его выходе и готов к работе с новым поведением.
Эффект пропуска последующей компиляции может усугубляться, когда вы переходите к большому решению - изменение комментария в {ProjectName}.Utilities.dll
больше не требует все строить!
Многие изменения включают в себя изменение как публичной поверхности API, так и внутренней реализации, поэтому это изменение не ускоряет все сборки, но ускоряет многие сборки.