VS Code/macOSで.NET MAUIを.NET 9へアップグレードする方法|ProjectReferenceが.NET 8のままに見える原因と対処

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-sdks9.x系が入っているか確認
MAUI workloadの状態確認dotnet workload listmaui関連が入っているか/古くないか確認
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のまま」になります。

項目よくある設定アップグレード時のポイント
TargetFrameworksnet8.0-android;net8.0-ios;…net9.0-android;net9.0-ios;…へ置換
WindowsターゲットOS条件付きで追加macOS上では条件付きのままにしておくと安全
UseMauitrueMAUIアプリでは基本維持

参照先:クラスライブラリ側を.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 restoreMAUI関連の不足を補う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>
ターゲットよく使う設定項目目的
iOSSupportedOSPlatformVersion最小対応OSを明確化してビルド要件を揃える
Mac CatalystSupportedOSPlatformVersion最小対応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 --info9.x系が選ばれている
global.json固定の有無リポジトリ直下を確認9.xに更新、または不要なら削除
net8.0の記述が残っていないかgrep -R "net8\.0" -n .必要な箇所以外はnet9.0へ置換済み
objが古くないかbin/obj削除後に再ビルド出力パスがnet9.0-xxxへ変わる
Workloadが追従しているかdotnet workload listmaui関連が揃っている

実務でおすすめの進め方(安全に.NET 9へ移行する手順)

チーム開発やCIがある前提なら、次の流れにすると“原因切り分け”がしやすくなります。

  1. 参照元(MAUIアプリ)と参照先(ライブラリ)のcsprojを両方、net9.0系へ揃える
  2. global.jsonがあるなら更新して、ローカルで.NET 9 SDKが使われる状態にする
  3. bin/objを削除して、restore/buildをやり直す
  4. まずは1つのターゲットだけ(例:Android)でビルドが通ることを確認する
  5. iOS/Mac Catalystまで順に確認し、詰まる場合はOSバージョン指定やXcode要件を見直す
  6. 最後に依存関係(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、この順番で進めてください。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次