VS Code(macOS)で.NET MAUIを.NET 9へ上げたいのに、参照中のクラスライブラリが「.NET 8のまま」に見えて不安になる場面があります。実はProjectReference自体を“8→9”に書き換えるのではなく、参照元と参照先のTargetFramework(TargetFrameworks)を揃えるのが解決の近道です。
前提:ProjectReferenceに「8/9」を書き換える場所は基本的にない
.NET(SDKスタイル)のプロジェクト参照は、参照設定そのもの(<ProjectReference />)に「.NET 8」「.NET 9」といったバージョン番号を持たないのが基本です。ProjectReferenceはあくまで「どのプロジェクト(.csproj)を参照するか」を示すだけで、実際にどのTargetFrameworkでビルドされるかは、参照元/参照先それぞれのcsprojに書かれたTargetFramework(s)で決まります。
そのため、VS Codeやビルドログで「参照しているライブラリがnet8.0っぽい」と見える場合、多くは次のどれかです。
- 参照先(クラスライブラリ)がまだnet8.0(またはnet8.0-android等)のまま
- 参照元(MAUIアプリ)がまだnet8.0-android等のまま
- global.jsonで.NET 8 SDKに固定されている
- 古いキャッシュ(bin/obj、NuGetキャッシュ、ワークロードの状態)に引っ張られている
- 稀に、ProjectReferenceにターゲットを固定するメタデータを入れている(後述)
| 「8のまま」に見える代表例 | 実際に見ているもの | 本当に直すべき場所 |
|---|---|---|
| bin/Debug/net8.0-android/… が残っている | 古いビルド成果物フォルダ | csprojのTargetFrameworks変更+bin/obj削除 |
| IntelliSenseがnet8.0前提っぽい | VS Code拡張のワークスペース状態/復元 | SDK確認+再読み込み+restore/clean |
| project.assets.jsonにnet8.0がある | 復元(restore)がnet8.0で走っている | global.json/SDK/TargetFrameworks/restoreの見直し |
最初に確認:.NET 9 SDKとMAUI workloadが揃っているか
csprojをいくら書き換えても、PC側のSDKやWorkloadが.NET 8のままだと「結果として8のまま」になります。VS Code中心で作業するなら、まずはターミナルで確認すると早いです。
dotnet --info
dotnet --list-sdks
dotnet workload list
| やること | コマンド例(macOS) | 狙い |
|---|---|---|
| SDKが.NET 9を認識しているか | dotnet --list-sdks | 9.x系が入っているか確認 |
| MAUI workloadの状態確認 | dotnet workload list | maui関連が入っているか/古くないか確認 |
| Workloadを更新 | dotnet workload update | .NET 9対応のコンポーネントへ更新 |
| Workloadを復元 | dotnet workload restore | リポジトリに応じたworkload状態へ揃える |
global.jsonで.NET 8に固定されていないか
意外と見落としがちな原因がglobal.jsonです。リポジトリ直下にglobal.jsonがあり、8.xに固定されていると、VS Codeのターミナルでもビルドでも.NET 8が選ばれ続けます。
{
"sdk": {
"version": "8.0.xxx"
}
}
この場合は、.NET 9 SDKに合わせるか、固定が不要なら削除します。固定したいなら例として以下のような形です(数字は環境に合わせてください)。
{
"sdk": {
"version": "9.0.xxx",
"rollForward": "latestFeature"
}
}
結論:変更すべきは参照元と参照先のTargetFramework(s)
質問で挙がっていた<TargetFramework>8.0.0</TargetFramework>のような書き方は、.NETのターゲット指定としては一般的ではありません。通常はTFM(Target Framework Moniker)として、次の形式で書きます。
- .NET 8:
net8.0 - .NET 9:
net9.0 - MAUI(Android):
net9.0-android - MAUI(iOS):
net9.0-ios - MAUI(Mac Catalyst):
net9.0-maccatalyst - MAUI(Windows):
net9.0-windows10.0.19041.0(例)
ここからは、参照元(MAUIアプリ)→参照先(クラスライブラリ)の順に、具体的な編集例を示します。
参照元:.NET MAUIアプリ側のcsprojを.NET 9に揃える
MAUIアプリは通常マルチターゲット(複数のTargetFrameworks)です。macOSで開発している場合、Android / iOS / Mac Catalystが中心になり、Windowsは条件付きで追加される構成がよくあります。
代表的な形(イメージ):
<Project Sdk="Microsoft.NET.Sdk">
net9.0-android;net9.0-ios;net9.0-maccatalyst
true
true
enable
enable
$(TargetFrameworks);net9.0-windows10.0.19041.0
このとき重要なのは、net8.0-xxx → net9.0-xxxへ揃えることです。MAUIアプリ側がnet8.0-androidのままだと、参照先ライブラリをnet9.0にしてもアプリがnet8でビルドされ続けるため、見た目も結果も「8のまま」になります。
| 項目 | よくある設定 | アップグレード時のポイント |
|---|---|---|
| TargetFrameworks | net8.0-android;net8.0-ios;… | net9.0-android;net9.0-ios;…へ置換 |
| Windowsターゲット | OS条件付きで追加 | macOS上では条件付きのままにしておくと安全 |
| UseMaui | true | MAUIアプリでは基本維持 |
参照先:クラスライブラリ側を.NET 9に上げる(ここが最重要)
「参照しているクラスライブラリを.NET 8 → .NET 9に上げたい」の本体は、参照先プロジェクトのTargetFramework(s)を上げることです。ここはライブラリの性質によって最適解が変わります。
パターンA:純粋な共通ロジック(プラットフォーム依存なし)なら net9.0 で十分
UIやプラットフォームAPI(Android/iOS固有API)を使わない、ドメイン/サービス/ユーティリティ中心のライブラリなら、TargetFrameworkをnet9.0にするのがシンプルです。MAUIアプリ(net9.0-android等)からも参照できます。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
パターンB:MAUI向けクラスライブラリ(MAUI APIやリソースを使う)なら net9.0-xxx でマルチターゲット
MAUIのコントロール/リソース/ハンドラー、あるいはプラットフォーム別分岐が必要なライブラリは、アプリと同様にマルチターゲットにしておくと迷いが減ります。いわゆる「MAUI Class Library」的な構成です。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks>
<UseMaui>true</UseMaui>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('windows'))">
<TargetFrameworks>$(TargetFrameworks);net9.0-windows10.0.19041.0</TargetFrameworks>
</PropertyGroup>
</Project>
パターンC:移行期間は net8.0 と net9.0 を併記して段階移行する
同じライブラリを.NET 8の別アプリも参照している、CIや配布都合で一気に上げられない、といった場合は、移行期間だけ二重ターゲットにしておくのが現実的です。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
ただし、MAUI向けにnet9.0-android等をターゲットにしているライブラリの場合は、同じ粒度でnet8.0-android と net9.0-androidを併記する構成になります。ビルド時間・依存関係が増えるので「本当に必要か」を判断して採用すると良いです。
| ライブラリの中身 | おすすめTargetFramework | 理由 |
|---|---|---|
| ビジネスロジックのみ(UI/OS依存なし) | net9.0 | 最小構成でMAUI全ターゲットから参照しやすい |
| MAUIの型やXAML、リソース、Handlerに依存 | net9.0-android;net9.0-ios;… | MAUIアプリのターゲットと揃えて齟齬を減らす |
| 移行期間で両方必要 | net8.0;net9.0(または各OSで併記) | 利用側を段階移行できる |
VS Codeで「参照を付け直す」やり方(GUI不要)
Visual Studio(IDE)なら参照追加をGUIでできますが、VS Code中心の場合はCLIでの参照追加が最も確実です。手作業でcsprojを編集する方法もありますが、まずはコマンドを推奨します。
方法1:dotnet add reference(推奨)
参照元(MAUIアプリ)のcsprojがあるディレクトリで実行します。
dotnet add path/to/YourMauiApp.csproj reference ../YourLib/YourLib.csproj
これで参照元csprojに、次のようなItemGroupが自動で追記されます。
<ItemGroup>
<ProjectReference Include="..\YourLib\YourLib.csproj" />
</ItemGroup>
方法2:csprojを直接編集してProjectReferenceを明示する
すでに参照があるのに不整合が起きている場合、「一度消して書き直す」意味で有効です。VS Code上で参照元csprojを開き、適切なパスで記述します。
<ItemGroup>
<ProjectReference Include="..\YourLib\YourLib.csproj" />
</ItemGroup>
ここで重要なのは、ProjectReferenceにバージョンを書かないことよりも、参照先csprojが.NET 9をターゲットしている状態に揃えることです。
方法3:ソリューション運用なら dotnet sln add も併用する
ソリューション(.sln)で管理している場合、slnにプロジェクトが入っていないと、VS Code側の認識やビルド操作で迷子になりがちです。
dotnet sln YourSolution.sln add ../YourLib/YourLib.csproj
| 操作 | おすすめ手段 | 向いている状況 |
|---|---|---|
| 参照を追加する | dotnet add ... reference ... | GUIなしで確実に参照を付けたい |
| 参照を「付け直す」 | csprojを編集してProjectReferenceを再記述 | パスや余計な設定を整理したい |
| ソリューションに追加 | dotnet sln add | 複数プロジェクト管理、CI/ビルド手順を揃えたい |
「ProjectReferenceが8のまま」と言われる本当の理由
“参照の再設定が必要”と言われがちですが、実務では次の2パターンに分解すると理解が早くなります。
- 参照設定が壊れている:ProjectReferenceのパスが間違っている、slnに入っていない、古い参照が残っている等
- 参照は正しいが、復元/ビルドが古い状態で走っている:global.json、SDK、workload、bin/obj、NuGetキャッシュ等
つまり、ビルドが通っているなら「参照の付け直し」は必須ではなく、むしろTargetFramework(s)の揃え込み+キャッシュ掃除の方が効果が大きいケースが多いです。
アップグレード直後に効く定番対処:bin/obj削除とrestore
.NETの復元(restore)は、TargetFrameworkごとに依存関係を解決し、obj/project.assets.jsonなどに結果を保存します。TargetFrameworkを変更した直後に古いobjが残っていると、「まだnet8.0で復元された情報」を参照してエラーになったり、VS Codeの表示が追従しなかったりします。
最短で効く手順
# リポジトリ直下で実行する想定(macOS)
find . -type d \( -name bin -o -name obj \) -prune -exec rm -rf {} +
# NuGetキャッシュも一掃したい場合(必要なときだけ)
dotnet nuget locals all --clear
# workload/restore/build を順に
dotnet workload restore
dotnet restore
dotnet build
| 対処 | 何が直るか | 注意点 |
|---|---|---|
| bin/obj削除 | 古いTFMの中間生成物・復元結果をリセット | 初回ビルドは時間がかかる |
| NuGetキャッシュクリア | 破損/不整合パッケージの再取得 | 社内レジストリ利用時は通信量に注意 |
| workload restore | MAUI関連の不足を補う | SDK更新後は特に有効 |
MAUI関連パッケージとCompatibilityの整理(必要な場合)
.NET 8→.NET 9移行で詰まりやすいのが、MAUI関連の依存関係ズレです。プロジェクトが古いテンプレート由来だったり、過去の移行でCompatibilityを抱えたままだったりすると、アップグレードで一気に表面化します。
Microsoft.Maui.Controls.Compatibility を使っている場合
Microsoft.Maui.Controls.Compatibilityは、移行用の互換レイヤーとして使われることがありますが、長期的には依存を減らすのが無難です。もし使っているなら、まずは以下を確認してください。
- 本当に必要な機能(旧Renderer依存、古いコントロール依存)が残っているか
- Handlerベース/新しい実装へ置き換えられないか
- 削除するとコンパイルが落ちる箇所がどこか(影響範囲の把握)
いきなり削除が不安なら、まずは「.NET 9に上げてビルドが通る状態」を作り、その後に段階的にCompatibilityを外すのがおすすめです。
依存関係の“最新版”を確認する
VS Codeでも、dotnet CLIで依存関係を洗い出せます。
dotnet list package --outdated
中央管理(Central Package Management)を使っている場合は、Directory.Packages.props側を更新する流れになります。例:
<Project>
<ItemGroup>
<PackageVersion Include="CommunityToolkit.Maui" Version="x.y.z" />
<PackageVersion Include="SomeLibrary" Version="a.b.c" />
</ItemGroup>
</Project>
iOS / Mac Catalystターゲットで必要になりがちなOSバージョン指定
iOSやMac Catalystをターゲットにしていると、アップグレード時にOSの最小サポートバージョン指定が原因で止まることがあります。プロジェクト構成やXcode/SDKの組み合わせで必須/不要が分かれますが、詰まったときの定番チェックポイントとして押さえておくと便利です。
例として、iOSやMac Catalystに対してSupportedOSPlatformVersionを明示するパターンです(値はプロジェクト要件に合わせて調整してください)。
<PropertyGroup Condition="$(TargetFramework.Contains('-ios'))">
<SupportedOSPlatformVersion>15.0</SupportedOSPlatformVersion>
</PropertyGroup>
<PropertyGroup Condition="$(TargetFramework.Contains('-maccatalyst'))">
<SupportedOSPlatformVersion>15.0</SupportedOSPlatformVersion>
</PropertyGroup>
| ターゲット | よく使う設定項目 | 目的 |
|---|---|---|
| iOS | SupportedOSPlatformVersion | 最小対応OSを明確化してビルド要件を揃える |
| Mac Catalyst | SupportedOSPlatformVersion | 最小対応OSを揃えてAPI互換性を確保 |
よくあるエラーと対処(.NET 9移行時)
移行時は「エラー文に対して、どこを直せばいいか」が見えにくいことがあります。代表的な症状と対処の対応表を置いておきます。
| 症状/エラーの雰囲気 | 原因の典型 | 対処の最短ルート |
|---|---|---|
| assets fileにnet9.0-androidが無い | restoreが古いTFMで走っている / TargetFrameworks未更新 | 参照元/参照先のTargetFrameworks見直し→bin/obj削除→restore |
| なぜか.NET 8 SDKでビルドされる | global.jsonで固定 / PATH上のdotnetが古い | global.json更新/削除→dotnet --list-sdks確認 |
| MAUI関連のビルドタスクで落ちる | workload未更新 / 依存関係ズレ | dotnet workload update→workload restore |
| iOS/Mac Catalystで最低OS要件関連のエラー | 最小サポートOS指定が不足/不一致 | SupportedOSPlatformVersionを条件付きで明示 |
| ProjectReferenceはあるのに型が見えない | 参照先が別TFMでビルドされている / キャッシュ | 参照先csprojのTargetFramework(s)更新→bin/obj削除→VS Code再読み込み |
稀な例:ProjectReferenceにターゲット固定が入っていないか
基本的にはProjectReferenceに「net8.0固定」のような情報は入りませんが、過去にビルド調整をしているリポジトリだと、ProjectReferenceにメタデータを付けて参照先のターゲットを固定しているケースがあります。
もし参照元csprojに、ProjectReference直下へ何らかのターゲット指定が入っている場合(例:SetTargetFramework相当の指定や、独自のMSBuildプロパティ)、「8固定」になっていないかを確認し、必要に応じて9へ揃えてください。
「まだ8に見える」時のチェックリスト(VS Code/macOS向け)
ここまでやっても表示や挙動が追従しないときは、次のチェックが効きます。
| チェック項目 | 確認方法 | 期待する状態 |
|---|---|---|
| SDKが.NET 9か | dotnet --info | 9.x系が選ばれている |
| global.json固定の有無 | リポジトリ直下を確認 | 9.xに更新、または不要なら削除 |
| net8.0の記述が残っていないか | grep -R "net8\.0" -n . | 必要な箇所以外はnet9.0へ置換済み |
| objが古くないか | bin/obj削除後に再ビルド | 出力パスがnet9.0-xxxへ変わる |
| Workloadが追従しているか | dotnet workload list | maui関連が揃っている |
実務でおすすめの進め方(安全に.NET 9へ移行する手順)
チーム開発やCIがある前提なら、次の流れにすると“原因切り分け”がしやすくなります。
- 参照元(MAUIアプリ)と参照先(ライブラリ)のcsprojを両方、net9.0系へ揃える
- global.jsonがあるなら更新して、ローカルで.NET 9 SDKが使われる状態にする
- bin/objを削除して、restore/buildをやり直す
- まずは1つのターゲットだけ(例:Android)でビルドが通ることを確認する
- iOS/Mac Catalystまで順に確認し、詰まる場合はOSバージョン指定やXcode要件を見直す
- 最後に依存関係(NuGet)を整理し、Compatibilityの削減など“品質改善”に着手する
この順に進めると、「参照の問題」なのか「環境の問題」なのか「依存関係の問題」なのかが混ざりにくく、VS Code中心でもスムーズに移行できます。
まとめ:ProjectReferenceを直すのではなく、TargetFrameworkを揃える
.NET MAUI(VS Code / macOS)で「参照しているクラスライブラリを.NET 8→.NET 9に上げたい」問題は、ほとんどの場合ProjectReferenceの再設定そのものではなく、参照元/参照先のTargetFramework(s)を.NET 9へ揃え、必要ならbin/obj削除などでキャッシュをリセットすることで解消します。
迷ったら、参照先(クラスライブラリ)のTargetFramework(s)を先に直す、次に参照元(MAUIアプリ)のTargetFrameworksを揃える、最後にrestore/clean、この順番で進めてください。

コメント