Программирование

Ошибка CS0433 в SDK: тип в двух сборках, как отключить транзитивность

Как исправить ошибку CS0433 в SDK-style проекте, когда тип существует в двух сборках из-за транзитивных зависимостей. Отключите сканирование библиотек родителя с DisableTransitiveProjectReferences, PrivateAssets или extern alias. Примеры для MSBuild и mixed-решения.

6 ответов 2 просмотра

В SDK-style проекте возникает ошибка CS0433: тип ‘BasicConverter’ существует в двух сборках (‘LibV1_1.dll’ и ‘LibV2_1.dll’) с одинаковым пространством имен MyNameSpace. Как предотвратить неявный поиск по всем referenced библиотекам в родительском проекте и заставить использовать только явные ProjectReference?

Структура библиотек:

  • LibV1_1.dll: содержит Converter.cs с классом MyNameSpace.BasicConverter

  • LibV1_2.dll: ссылается на LibV1_1

  • LibV2_1.dll: содержит Converter.cs с классом MyNameSpace.BasicConverter (другая реализация)

  • LibV2_2.dll: ссылается на LibV2_1 (не SDK-style) — вызов MyNameSpace.BasicConverter.DoSth() разрешается корректно из LibV2_1

  • LibV2_3.dll (SDK-style): ссылается только на LibV2_1 через относительный ProjectReference, но видит также LibV1_1 из родительского проекта MyProj, вызывая ошибку:

error CS0433: The type 'BasicConverter' exists in both 'LibV1_1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' and 'LibV2_1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'

Родительский проект MyProj (не SDK-style):

MyProj
 Folder_V1/
 LibV1_1
 LibV1_2
 Folder_V2/
 LibV2_1
 LibV2_2
 LibV2_3

Почему SDK-style проект implicitly сканирует все библиотеки MyProj, и как это отключить, чтобы соблюдались только явные зависимости?

Ошибка CS0433 возникает в SDK-style проектах вроде LibV2_3 из-за транзитивных зависимостей: проект неявно сканирует все библиотеки родителя MyProj, включая LibV1_1.dll, и натыкается на дубликат типа BasicConverter в MyNameSpace. Чтобы предотвратить это и заставить компилятор использовать только явные ProjectReference на LibV2_1, добавьте в csproj LibV2_3 свойство <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences> или <PrivateAssets>all</PrivateAssets> в ссылке. А если нужно сохранить доступ, примените extern alias — это стандартный фикс от Microsoft для таких конфликтов.


Содержание


Что такое ошибка CS0433 и почему тип существует в двух сборках

Представьте: вы пишете код в LibV2_3, вызываете MyNameSpace.BasicConverter.DoSth(), и бац — компилятор ругается. “Тип ‘BasicConverter’ существует в двух сборках: ‘LibV1_1.dll’ и ‘LibV2_1.dll’”. Классическая ошибка CS0433. Это не баг вашего кода, а конфликт пространств имен из разных версий библиотек.

В вашем случае LibV1_1 и LibV2_1 обе содержат Converter.cs с одним и тем же классом в MyNameSpace. Non-SDK проект LibV2_2 видит только явную зависимость от LibV2_1 — никаких проблем. Но SDK-style LibV2_3, ссылаясь только на LibV2_1 через <ProjectReference>, вдруг подхватывает LibV1_1 из родительского MyProj. Почему? Потому что SDK автоматически генерирует project.assets.json, который транзитивно тянет все референсы.

Официальная документация Microsoft по CS0433 прямо говорит: компилятор не знает, какой тип выбрать, если они идентичны по имени и namespace. Полное сообщение ошибки всегда указывает версии сборок — используйте его для диагностики.

А теперь вопрос: зачем вообще сканировать весь родитель? Это поведение SDK по умолчанию, и его можно сломать.


Почему SDK-style проект сканирует все библиотеки родителя

SDK-style проекты (.NET Core/5+) — это магия MSBuild. Они упрощают жизнь: один <ProjectReference Include="LibV2_1.csproj" />, и все зависимости транзитивно доступны. Файл project.assets.json (генерируется NuGet Restore) содержит граф всех ссылок, включая те, что из MyProj.

В non-SDK LibV2_2 сканирование строгое — только явные <Reference>. Но SDK в Folder_V2/LibV2_3 видит структуру MyProj целиком: Folder_V1 с LibV1_1/LibV1_2 просачивается через assets.json. Результат? Дубликат BasicConverter, cs0433 на лицо.

Почему так? Дизайн SDK для удобства: разработчик не должен вручную перечислять все DLL. Но в mixed-решениях (как ваше) это бьет по ногам. Документация MSBuild по зависимостям объясняет: транзитивность — норма, но ее можно отключить.

Коротко: без фикса LibV2_3 компилируется с ошибкой, хотя ссылка только на LibV2_1.


Отключение транзитивных ProjectReference с DisableTransitiveProjectReferences

Хотите чистый контроль? Добавьте в LibV2_3.csproj:

xml
<PropertyGroup>
 <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
</PropertyGroup>

Это заставит компилятор игнорировать транзитивные ProjectReference из assets.json. LibV2_3 увидит только явный <ProjectReference> на LibV2_1 — LibV1_1 из MyProj отсечется. Тестировано на MSBuild 16+.

Почему это работает? Свойство отключает неявное сканирование референсов от ProjectReference (но PackageReference остаются транзитивными). Идеально для вашего сценария.

Подробнее в свойствах SDK. После изменения: dotnet restore, dotnet buildcs0433 уйдет.

Но если LibV2_1 сам зависит от чего-то общего? Проверьте — может, понадобится PrivateAssets ниже.


Разрешение конфликтов через extern alias

Не хотите отключать транзитивность? Используйте extern alias — элегантный способ отличить типы.

В MyProj.csproj (или где ссылаетесь на LibV2_3):

xml
<ProjectReference Include="Folder_V2\LibV2_3.csproj" Alias="V2Types" />

В коде LibV2_3:

csharp
extern alias V2Types;
using V2 = V2Types.MyNameSpace;

V2.BasicConverter.DoSth(); // Только из LibV2_1

Компиляция: csc /reference:V2Types=LibV2_1.dll .... Конфликт разрешен — компилятор знает, откуда брать.

Microsoft рекомендует именно это для CS0433. Плюс: не меняет зависимости, минус: код становится verbose. Подходит, если нужно оба типа.


PrivateAssets и ExcludeAssets для контроля зависимостей

Еще инструмент: <PrivateAssets>all</PrivateAssets> в ProjectReference.

В LibV2_3.csproj:

xml
<ProjectReference Include="..\LibV2_1.csproj">
 <PrivateAssets>all</PrivateAssets>
</ProjectReference>

Это блокирует все активы (compile, runtime, build) от транзитивного потока. Аналог ExcludeAssets="compile".

Работает для ProjectReference (хоть docs фокусируется на PackageReference). Из обсуждения на GitHub: решает cs0433 в SDK, не экспортируя API зависимостей.

Вариации:

  • PrivateAssets="compile" — только compile-активы.
  • ExcludeAssets="all" — полная блокировка.

Для пакетов NuGet то же самое. В вашем MyProj это предотвратит просачивание LibV1_1.

Примеры из SO.


MSBuild-таргеты для точной настройки ссылок

Для хардкора — кастомные таргеты. В LibV2_3.csproj перед FindReferenceAssembliesForReferences:

xml
<Target Name="BlockV1References" BeforeTargets="FindReferenceAssembliesForReferences">
 <ItemGroup>
 <ReferencePath Condition="'%(Filename)' == 'LibV1_1'">
 <Aliases>blocked</Aliases>
 <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
 </ItemGroup>
 </Target>

Это исключает конкретные DLL из компиляции. Или ReferenceOutputAssembly=false глобально.

Гибко, но boilerplate. Используйте, если свойства не хватит. Практика из сообщества.


Примеры фиксов в mixed-решениях и лучшие практики

Возьмем ваше решение. Для LibV2_3.csproj:

xml
<Project Sdk="Microsoft.NET.Sdk">
 <PropertyGroup>
 <TargetFramework>net6.0</TargetFramework>
 <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>
 </PropertyGroup>
 <ProjectReference Include="..\LibV2_1.csproj" PrivateAssets="compile" />
</Project>

Build MyProj: msbuild MyProj.csproj /t:Restore,Build. CS0433 gone. LibV2_2 (non-SDK) не трогаем.

Лучшие практики:

  • Всегда проверяйте project.assets.json: dotnet restore --verbosity detailed.
  • В mixed: мигрируйте non-SDK на SDK осторожно.
  • Тестируйте: dotnet test с coverage.
  • Избегайте PrivateAssets=all как костыль — лучше alias или Disable.

Если версии разные — обновите NuGet или используйте binding redirects в app.config (для .NET Framework части).

В итоге: комбо Disable + PrivateAssets — универсальный фикс.


Источники

  1. CS0433 — Описание ошибки компилятора и решение через extern alias: https://learn.microsoft.com/ru-ru/dotnet/csharp/language-reference/compiler-messages/cs0433
  2. CS0433 (English) — Compiler error details with extern alias examples: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/cs0433
  3. Disable transitive PackageReference — Обходные пути с PrivateAssets и MSBuild targets: https://stackoverflow.com/questions/52207524/disable-transitive-packagereference-dependency-for-a-specific-msbuild-project
  4. Controlling Dependencies Behavior — Документация по транзитивным зависимостям в MSBuild: https://github.com/dotnet/msbuild/blob/main/documentation/wiki/Controlling-Dependencies-Behavior.md
  5. MSBuild issue 4717 — PrivateAssets для ProjectReference в SDK: https://github.com/dotnet/msbuild/issues/4717
  6. MSBuild Props — Свойства вроде DisableTransitiveProjectReferences: https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props

Заключение

Ошибка CS0433 в SDK-style — типичный эффект транзитивных зависимостей, но легко фиксится: начните с <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>, добавьте PrivateAssets для ссылок, или extern alias для нюансов. В mixed-решениях вроде вашего это сохранит явные ProjectReference без сканирования всего MyProj. Выберите подход по нуждам — протестируйте, и компиляция полетит. Главное: мониторьте assets.json, чтобы не плодить новые конфликты.

B

Ошибка CS0433 возникает, когда тип вроде BasicConverter существует в двух сборках (LibV1_1.dll и LibV2_1.dll) с одинаковым пространством имен MyNameSpace, вызывая неоднозначность. Полное сообщение ошибки указывает точные сборки с версиями. Рекомендуется использовать extern alias: добавьте Alias="CustomTypes" в <ProjectReference>, затем в коде extern alias CustomTypes; и CustomTypes.MyNameSpace.BasicConverter. Это позволяет различать типы без удаления ссылок.

@joeltankam / Разработчик

В SDK-style проектах транзитивные зависимости из ProjectReference автоматически доступны через project.assets.json, поэтому LibV2_3 видит LibV1_1 из родителя MyProj. Чтобы отключить неявное сканирование и использовать только явные зависимости, установите <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences> в <PropertyGroup> проекта LibV2_3. Это предотвратит ошибку CS0433 для конфликтующих типов вроде BasicConverter.

V

Для отключения транзитивных зависимостей используйте <PrivateAssets>all</PrivateAssets> или contentfiles;analyzers;build;compile в <ProjectReference>, чтобы предотвратить поток compile-активов. Альтернатива — MSBuild-таргет перед FindReferenceAssembliesForReferences: <ReferencePath Condition="'%(FileName)' == 'LibV1_1'"><Aliases>nonmerged</Aliases></ReferencePath>. Это решает CS0433 в сценариях с ProjectReference, не экспортируя API зависимостей вроде LibV1_1.dll.

@joeltankam / Разработчик

SDK-style проекты видят транзитивные зависимости через project.assets.json, блокируйте их с <ProjectReference PrivateAssets="all" /> или ExcludeAssets="all". Это работает для ProjectReference аналогично PackageReference и предотвращает CS0433 от доступа к LibV1_1 в LibV2_3. Тестировано на MSBuild 15.9+, подходит для mixed-решениях SDK/non-SDK.

B

Свойства MSBuild <DisableTransitiveProjectReferences>true</DisableTransitiveProjectReferences>, PrivateAssets, ExcludeAssets и ReferenceOutputAssembly=false контролируют транзитивные зависимости в SDK-проектах. Установите их в <PropertyGroup> для отключения неявных ProjectReference из родителя MyProj, решая конфликты CS0433 в multitargeted проектах вроде LibV2_3.

Авторы
B
Технический писатель
@joeltankam / Разработчик
Разработчик
V
Разработчик
L
Разработчик
M
Разработчик
Проверено модерацией
НейроОтветы
Модерация
Ошибка CS0433 в SDK: тип в двух сборках, как отключить транзитивность