Базовые сборки

Базовые сборки являются особым типом сборки, которая содержит только минимальный объем метаданных, необходимый для представления общедоступного API-интерфейса библиотеки. Такие сборки включают в себя объявления для всех элементов, которые важны при указании ссылки на сборку в средствах сборки, но исключают все реализации элементов, а также объявления закрытых элементов, не имеющих наблюдаемого влияния на их контракт API. Обычные сборки, напротив, называются сборками реализации.

Базовые сборки не могут быть загружены для выполнения, но они могут передаваться как входные данные компилятора так же, как сборки реализации. Базовые сборки обычно распределяются с помощью пакета средств разработки программного обеспечения (SDK) определенной платформы или библиотеки.

Использование базовой сборки позволяет разработчикам создавать программы, предназначенные для конкретной версии библиотеки, без наличия полной сборки реализации для этой версии. Допустим, на компьютере установлена только последняя версия определенной библиотеки, но требуется создать программу, предназначенную для более ранней версии этой библиотеки. При компиляции непосредственно из сборки реализации вы можете случайно использовать элементы API, которые недоступны в более ранней версии, и обнаружите эту ошибку только при проверке программы на целевом компьютере. При компиляции из базовой сборки для более ранней версии сразу же возникает ошибка времени компиляции.

Базовая сборка также может представлять контракт, то есть набор API-интерфейсов, который не соответствует конкретной сборке реализации. Такие базовые сборки, называемые сборками контракта, можно использовать для нескольких платформ, поддерживающих один и тот же набор API-интерфейсов. Например, .NET Standard предоставляет сборку контракта, netstandard.dll, которая представляет набор общих API-интерфейсов, совместно используемых различными платформами .NET. Реализации этих API-интерфейсов содержатся в разных сборках на разных платформах, например mscorlib.dll в .NET Framework или System.Private.CoreLib.dll в .NET Core. Библиотека, нацеленная на .NET Standard, может выполняться на всех платформах, поддерживающих .NET Standard.

Использование базовых сборок

Чтобы использовать определенные API-интерфейсы из проекта, необходимо добавить ссылки на их сборки. Ссылки можно добавлять либо в сборки реализации, либо в базовые сборки. По возможности рекомендуется использовать базовые сборки если они доступны. Таким образом, вы будете гарантированно использовать в целевой версии только поддерживаемые члены API, которые были предназначены для этого разработчиками API. Применяя базовую сборку, вы гарантированно не будете зависеть от деталей реализации.

Базовые сборки для библиотек .NET Framework распределяются с помощью пакетов нацеливания. Их можно получить, скачав автономный установщик или выбрав компонент в Visual Studio Installer. См. дополнительные сведения об установке .NET Framework для разработчиков. Для .NET Core и .NET Standard базовые сборки автоматически скачиваются при необходимости (через NuGet) и на них указываются ссылки. Для .NET Core 3.0 и более поздних версий базовые сборки для основной платформы находятся в пакете Microsoft.NETCore.App.Ref (до версии 3.0 используется пакет Microsoft.NETCore.App).

При добавлении ссылок на сборки .NET Framework в Visual Studio с помощью диалогового окна Добавление ссылки вы выбираете сборку из списка, и Visual Studio автоматически находит базовые сборки, соответствующие требуемой версии .NET Framework, выбранной в проекте. То же самое применимо для добавления ссылок непосредственно в проект MSBuild с помощью элемента проекта Reference. Вам необходимо указать только имя сборки, а не полный путь к файлу. При добавлении ссылок на эти сборки в командной строке с помощью параметра компилятора -reference (в C# и в Visual Basic) или с помощью метода Compilation.AddReferences в API Roslyn необходимо вручную указать файлы базовой сборки для правильной версии целевой платформы. Файлы базовой сборки .NET Framework находятся в каталоге %ProgramFiles(x86)%\Reference Assemblies\Microsoft\Framework\.NETFramework. Для .NET Core можно принудительно выполнить операцию публикации, чтобы скопировать базовые сборки для целевой платформы в подкаталог publish/refs выходного каталога, установив для свойства проекта PreserveCompilationContext значение true. Затем можно передать эти файлы базовой сборки компилятору. Для поиска их путей можно использовать DependencyContext из пакета Microsoft.Extensions.DependencyModel.

Так как базовые сборки не содержат реализации, они не могут быть загружены для выполнения. Любые попытки сделать это приведут к возникновению исключения System.BadImageFormatException. Если вам нужно изучить содержимое базовой сборки, ее можно загрузить в контекст только для отражения в .NET Framework (с помощью метода Assembly.ReflectionOnlyLoad) или в MetadataLoadContext в .NET Core.

Создание базовых сборок

Рекомендуется создавать базовые сборки для библиотек в тех случаях, когда их пользователям требуется создавать программы на основе множества разных версий отдельной библиотеки. Распространение сборок реализации для всех этих версий может оказаться нецелесообразным из-за их большого размера. Базовые сборки имеют меньший размер, поэтому их распространение в составе пакета SDK для библиотеки уменьшает объем скачиваемых данных и экономит место на диске.

Интегрированные среды разработки и средства сборки также могут использовать базовые сборки, чтобы сократить время сборки в случае больших решений, состоящих из нескольких библиотек классов. Как правило, в сценариях инкрементной сборки проект перестраивается при изменении любого из его входных файлов, включая сборки, от которых он зависит. Сборка реализации изменяется каждый раз, когда программист изменяет реализацию любого элемента. Базовая сборка изменяется только при ее влиянии на общедоступный API. Таким образом, использование базовой сборки в качестве входного файла вместо сборки реализации позволяет в некоторых случаях пропустить сборку зависимого проекта.

Вы можете создавать базовые сборки:

Если хотите распределить базовые сборки с помощью пакетов NuGet, их необходимо включить в подкаталог ref\ каталога пакета, а не в подкаталог lib\ , используемый для сборок реализации.

Структура базовых сборок

Базовые сборки являются расширением связанной концепции — сборок, содержащих только метаданные. Тела методов в сборках, состоящих только из метаданных, заменяются одним телом throw null, но такие сборки включают все члены, кроме анонимных типов. Тело throw null используется для того, чтобы могло выполняться средство PEVerify для проверки полноты метаданных, что было бы невозможно при отсутствии тела.

Базовые сборки удаляют метаданные (закрытые члены) из сборок, содержащих только метаданные.

  • Базовая сборка содержит ссылки только на необходимые компоненты в слое доступа API. Реальная сборка может иметь дополнительные ссылки, связанные с конкретной реализацией. Например, базовая сборка для class C { private void M() { dynamic d = 1; ... } } не ссылается на типы, требуемые для dynamic.
  • Закрытые функции-члены (методы, свойства и события) удаляются в случае, если их удаление не скажется заметно на компиляции. Если нет атрибутов InternalsVisibleTo, внутренние элементы функции также удаляются.

Метаданные в базовых сборках продолжают хранить следующие сведения:

  • все типы, включая закрытые и вложенные;
  • все атрибуты, даже внутренние;
  • все виртуальные методы;
  • явные реализации интерфейса;
  • явно реализованные свойства и события, так как их методы доступа являются виртуальными;
  • все поля структур.

Базовые сборки содержат атрибут ReferenceAssembly уровня сборки. Этот атрибут может быть задан в исходном коде, в таком случае компилятору не требуется его синтезировать. Из-за этого атрибута среды выполнения не будут загружать базовые сборки для выполнения (однако они могут загружаться в режиме только для отражения).

Точные сведения о структуре базовой сборки зависят от версии компилятора. Более новые версии могут исключить дополнительные метаданные, если они не влияют на общедоступный API-интерфейса.

Примечание

Сведения в этом разделе применимы только к базовым сборкам, которые созданы компиляторами Roslyn, начиная с C# версии 7.1 или Visual Basic версии 15.3. Структура базовых сборок для библиотек .NET Framework и .NET Core может отличаться некоторыми деталями, так как они используют собственный механизм создания базовых сборок. Например, они могут иметь совершенно пустые тела методов вместо тела throw null. Но общие принципы по-прежнему применяются: у них нет пригодных для использования реализаций методов и они содержат метаданные только для элементов, которые имеют наблюдаемый эффект с точки зрения общедоступного API-интерфейса.

См. также