はじめに
お久しぶりです。最近ブログ更新サボってましたが、不定期になりますがまた書いていきたいと思います。
SourceGeneratorのRegisterPostInitializationOutputで属性生成してると、InternalsVisibleTo使ったときに型競合(CS0436)が起こる問題があり、その対応として.NET10から[Embedded]が入ってる
— はなちる@ゲーム制作 (@hanaaaaaachiru) March 7, 2026
ただUnityは最新でもMicrosoft.CodeAnalysis.CSharp v4.0.4使ってて、4.13.0(なはず)以上に上げてもらわない
今回はSourceGeneratorとInternalsVisibleToを組み合わせたときに型競合(CS0436)が起こる問題について書きたいと思います。
概要
SourceGeneratorのRegisterPostInitializationOutputで属性生成するのは定番の手法です。例えばCySharpさんのUnitGeneratorでもUnitOfAttributeを生成しています。
ただInternalVisiableToを組み合わせると型競合(CS0436)が起こってしまう問題が存在します。具体的には、以下のような構成にすると警告がでてきます。
<!-- SampleConsole.csproj --> <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net10.0</TargetFramework> </PropertyGroup> <!-- UnitGeneratorを利用している --> <ItemGroup> <PackageReference Include="UnitGenerator" Version="2.0.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> <!-- ClassLibrary1.csprojへ依存している。InternalVisibleToによりinternalにもアクセス可 --> <ItemGroup> <ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" /> </ItemGroup> </Project>
<!-- ClassLibrary1.csproj --> <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net10.0</TargetFramework> </PropertyGroup> <!-- UnitGeneratorを利用している --> <ItemGroup> <PackageReference Include="UnitGenerator" Version="2.0.0"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> <!-- SampleConsoleからClassLibrary1のinternalに対してアクセス可能にする --> <ItemGroup> <InternalsVisibleTo Include="SampleConsole" /> </ItemGroup> </Project>
# 実際にでてくる警告 $ dotnet build 'C:\...\UnitOfAttribute.g.cs' の型 'UnitOfAttribute<T>' は、'ClassLibrary1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' のインポートされた型 'UnitOfAttribute<T>' と競合しています。'C:\...\UnitOfAttribute.g.cs' で定義された型を使用しています。
つまりはどちらのアセンブリにも同名のinternalな属性が定義されていて、InternalVisibleToを利用すると競合を起こすということです。
解決策
.Net10以降
この問題に対してちゃんと対策をしてくれています。それが[Embedded]属性です。
github.com
API Proposal: `IncrementalGeneratorPostInitializationContext.AddEmbeddedAttributeDefinition` · Issue #76584 · dotnet/roslyn · GitHub
具体的には
- 生成する属性に対して
[global::Microsoft.CodeAnalysis.Embedded]を付与する RegisterPostInitializationOutputの中でAddEmbeddedAttributeDefinition()を実行する
をすることで解決できます。
context.RegisterPostInitializationOutput(static postInitializationContext => postInitializationContext.AddEmbeddedAttributeDefinition(); postInitializationContext.AddSource("myGeneratedFile.cs", SourceText.From(""" using System; using Microsoft.CodeAnalysis; namespace GeneratedNamespace { [AttributeUsage(AttributeTargets.Method), Embedded] internal sealed class GeneratedAttribute : Attribute { } } """, Encoding.UTF8)));
.Net10以前
属性を共有DLLとして定義して、それを参照するようにするという手法が挙げられます。例えばNetEscapades.EnumGeneratorsというOSSでは、NetEscapades.EnumGenerators.Attributesを用意しています。
具体的な手法は以下の記事でまとめられているのでチェックしてみてください。
Unityの場合
現在(2026/3/25)、Unityは最新でもMicrosoft.CodeAnalysis.CSharp v4.0.4使ってて、4.13.0以上(4.14.0かも?)に上げてもらわないと先ほどの手法を利用することができないはずです。
ですのでUnity対応を考えると、結局使うことができずcsc.rspやpragmaで暫定対応するしかないという...ということになりつらいなぁという感じでした。