dotnet-native-aot について
.NET 8 以降の開発者向け Native AOT コンパイルガイドです。公開設定、トリミング分析、型の保持、リフレクションを避ける実装、バイナリサイズの最適化に加え、P/Invoke と自己完結型デプロイも扱います。
主な機能
- 01Native AOT の公開設定
- 02AOT とトリミングの診断アナライザー
- 03ILLink 記述子による型の保持
- 04リフレクションを避ける実装パターン
- 05P/Invoke とバイナリの最適化
- 06自己完結型コンテナデプロイ
活用シーン
- 01コンソールアプリの Native AOT 公開設定
- 02AOT とトリミングの互換性問題の確認
- 03リフレクションで必要な型の保持
- 04AOT バイナリとコンテナのサイズ削減
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-native-aotインストール後、Claude Code で/dotnet-native-aotを入力して読み込まれたか確認します。
詳細
dotnet-native-aot
.NET 8 以降のアプリケーション向け、Native AOT コンパイルの完全なパイプラインです。PublishAot の構成、型を保持するための ILLink 記述子 XML、リフレクションを使わないコーディングパターン、P/Invoke に関する考慮事項、バイナリサイズの最適化、runtime-deps ベースイメージを使用した自己完結型デプロイ、診断アナライザー(EnableAotAnalyzer/EnableTrimAnalyzer)を扱います。
バージョンに関する前提: .NET 8.0 以降を基準とします。ASP.NET Core Minimal API とコンソールアプリ向けの Native AOT は、.NET 8 で提供されました。.NET 9 ではトリミング警告とライブラリの互換性が改善されました。.NET 10 ではリクエストデリゲートジェネレーターが強化され、Minimal API の AOT サポートが拡張されました。
対象範囲: このスキルでは、.NET Native AOT コンパイル全般、つまり発行パイプライン、MSBuild の構成、型の保持、P/Invoke、サイズ最適化を扱います。MAUI 固有の iOS/Mac Catalyst AOT(発行プロファイル、プラットフォーム固有のライブラリ互換性)については、[skill:dotnet-maui-aot] を参照してください。AOT を前提としたアプリケーション設計パターン(リフレクションよりソース生成を優先する、DI、シリアル化の選択肢)は [skill:dotnet-aot-architecture] で扱います。トリミングセーフなライブラリの作成については [skill:dotnet-trimming] を参照してください。Blazor/Uno 向け WASM AOT については [skill:dotnet-aot-wasm] を参照してください。
対象外: MAUI iOS/Mac Catalyst AOT パイプラインについては、[skill:dotnet-maui-aot] を参照してください。ソースジェネレーターの作成(Roslyn API)については、[skill:dotnet-csharp-source-generators] を参照してください。DI コンテナーのパターンについては、[skill:dotnet-csharp-dependency-injection] を参照してください。シリアル化の詳細については、[skill:dotnet-serialization] を参照してください。コンテナーのデプロイオーケストレーションについては、[skill:dotnet-containers] を参照してください。
相互参照:[skill:dotnet-aot-architecture] は AOT を前提とした設計パターン、[skill:dotnet-trimming] はトリミングセーフなライブラリの作成、[skill:dotnet-aot-wasm] は WebAssembly AOT、[skill:dotnet-maui-aot] は MAUI 固有の AOT、[skill:dotnet-containers] は runtime-deps ベースイメージ、[skill:dotnet-serialization] は AOT セーフなシリアル化、[skill:dotnet-csharp-source-generators] は AOT を可能にするソース生成、[skill:dotnet-csharp-dependency-injection] は AOT セーフな DI、[skill:dotnet-native-interop] は一般的な P/Invoke パターンとクロスプラットフォームのライブラリ解決を扱います。
PublishAot の構成
Native AOT を有効にする
<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>
# Publish as Native AOT
dotnet publish -c Release -r linux-x64
# Publish for specific targets
dotnet publish -c Release -r win-x64
dotnet publish -c Release -r osx-arm64
MSBuild プロパティ:アプリとライブラリ
アプリとライブラリでは異なる MSBuild プロパティを使用します。混在させないでください。
アプリケーションの場合(コンソールアプリ、ASP.NET Core Minimal API):
<PropertyGroup>
<PublishAot>true</PublishAot>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>
ライブラリの場合(NuGet パッケージ、共有クラスライブラリ):
<PropertyGroup>
<IsAotCompatible>true</IsAotCompatible>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
IsAotCompatible と IsTrimmable を設定すると、それぞれ AOT アナライザーとトリムアナライザーが自動的に有効になります。ライブラリはスタンドアロン実行可能ファイルとして発行されないため、ライブラリプロジェクトでは PublishAot も設定しないでください。
診断アナライザー
発行前に問題を検出できるよう、開発中に AOT アナライザーとトリムアナライザーを有効にします。
<PropertyGroup>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>
発行せずに解析する
完全な発行を行わずに、dotnet build 中に解析を実行します。
# Analyze AOT compatibility without publishing
dotnet build /p:EnableAotAnalyzer=true /p:EnableTrimAnalyzer=true
# See per-occurrence warnings (not grouped by assembly)
dotnet build /p:EnableAotAnalyzer=true /p:EnableTrimAnalyzer=true /p:TrimmerSingleWarn=false
これにより、ネイティブバイナリを生成せずに IL2xxx(トリム)と IL3xxx(AOT)の警告が報告され、開発中にすばやくフィードバックを得られます。
よくある診断コード
| コード | カテゴリ | 意味 |
|---|---|---|
| IL2026 | トリム | メンバーに [RequiresUnreferencedCode] が指定されています。トリミング後に問題が発生する可能性があります |
| IL2046 | トリム | 基底型と派生型の間でトリム属性が一致していません |
| IL2057-IL2072 | トリム | トリマーが解析できない、さまざまなリフレクションの使用 |
| IL3050 | AOT | メンバーに [RequiresDynamicCode] が指定されています。実行時にコードを生成します |
| IL3051 | AOT | [RequiresDynamicCode] 属性が一致していません |
型の保持に使用する ILLink 記述子
トリマーが静的に解析できないリフレクションをコードで使用する場合は、型を保持するために ILLink 記述子 XML を使用します。従来の RD.xml は使用しないでください。これは .NET Native/UWP 形式であり、最新の .NET AOT では無視されます。
ILLink 記述子 XML
<linker>
<assembly fullname="MyApp">
<type fullname="MyApp.Models.LegacyConfig" preserve="all" />
<type fullname="MyApp.Services.PluginLoader">
<method name="LoadPlugin" />
</type>
</assembly>
<assembly fullname="IncompatibleLibrary" preserve="all" />
</linker>
<ItemGroup>
<TrimmerRootDescriptor Include="ILLink.Descriptors.xml" />
</ItemGroup>
[DynamicDependency] 属性
コード内で対象を絞って保持する場合(小規模で局所的なケースでは ILLink XML よりも推奨):
using System.Diagnostics.CodeAnalysis;
// Preserve a specific method
[DynamicDependency(nameof(LegacyConfig.Initialize), typeof(LegacyConfig))]
public void ConfigureApp() { /* ... */ }
// Preserve all public members
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(PluginBase))]
public void LoadPlugins() { /* ... */ }
どれを使うべきか
| シナリオ | アプローチ |
|---|---|
| 1〜2 個のメソッド/型 | [DynamicDependency] 属性 |
| アセンブリ全体または多数の型 | ILLink 記述子 XML |
| AOT セーフでないサードパーティライブラリ | ILLink 記述子 XML または <TrimmerRootAssembly> |
| リフレクションを解析できる自作コード | ソースジェネレーターにリファクタリングする(長期的に最善) |
リフレクションを使用しないパターン
Native AOT は、実行時リフレクションを完全に避けるコードで最も効果を発揮します。リフレクションのパターンを、コンパイル時に利用できる代替手段に置き換えてください。
| リフレクションのパターン | AOT セーフな代替手段 |
|---|---|
マッピングに Activator.CreateInstance<T>() を使用 | ファクトリメソッドまたは明示的な new T() |
マッピングに Type.GetProperties() を使用 | Mapperly ソースジェネレーターまたは手動マッピング |
DI スキャンに Assembly.GetTypes() を使用 | 明示的な services.AddScoped<T>() |
JsonSerializer.Deserialize<T>(json) | JsonSerializer.Deserialize(json, Context.Default.T) |
ディスパッチに MethodInfo.Invoke() を使用 | 型に対する switch またはインターフェイスによるディスパッチ |
包括的な AOT ファーストの設計パターンについては、[skill:dotnet-aot-architecture] を参照してください。
P/Invoke に関する考慮事項
ネイティブライブラリを呼び出す P/Invoke(プラットフォーム呼び出し)は、通常 Native AOT で動作しますが、注意が必要です。
直接 P/Invoke(推奨)
// Direct P/Invoke -- AOT-compatible, no runtime marshalling overhead
[LibraryImport("libsqlite3", EntryPoint = "sqlite3_open")]
internal static partial int Sqlite3Open(
[MarshalAs(UnmanagedType.LPStr)] string filename,
out nint db);
[DllImport] の代わりに [LibraryImport](.NET 7 以降)を使用してください。ソースジェネレーターによってコンパイル時にマーシャリングコードが生成されるため、完全に AOT 互換になります。
DllImport と LibraryImport の比較
| 属性 | AOT 互換性 | マーシャリング |
|---|---|---|
[DllImport] | 部分的。一部のマーシャリングには実行時コード生成が必要 | 実行時マーシャリング |
[LibraryImport] | 完全。コンパイル時のソース生成 | コンパイル時マーシャリング |
// Migrate from DllImport to LibraryImport
// Before:
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
// After:
[LibraryImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool CloseHandle(IntPtr hObject);
ネイティブライブラリのデプロイ
Native AOT として発行する場合、ネイティブライブラリ(.so、.dylib、.dll)をバイナリと同じ場所に配置する必要があります。
<ItemGroup>
<NativeLibrary Include="libs/libcustom.so" />
</ItemGroup>
サイズの最適化
バイナリサイズを削減するオプション
<PropertyGroup>
<PublishAot>true</PublishAot>
<StripSymbols>true</StripSymbols>
<OptimizationPreference>Size</OptimizationPreference>
<InvariantGlobalization>true</InvariantGlobalization>
<StackTraceSupport>false</StackTraceSupport>
<EventSourceSupport>false</EventSourceSupport>
</PropertyGroup>
一般的なバイナリサイズ
| 構成 | コンソールアプリ | ASP.NET Minimal API |
|---|---|---|
| 既定の AOT | 約 10~15 MB | 約 15~25 MB |
| + StripSymbols | 約 8~12 MB | 約 12~20 MB |
| + サイズ最適化 | 約 6~10 MB | 約 10~18 MB |
| + InvariantGlobalization | 約 4~8 MB | 約 8~15 MB |
サイズ分析
# Analyze what contributes to binary size
dotnet publish -c Release -r linux-x64 /p:PublishAot=true
# Use sizoscope (community tool) for detailed size analysis
# https://github.com/AdrianEddy/sizoscope
runtime-deps を使用した自己完結型デプロイ
Native AOT は、.NET ランタイムを含む自己完結型バイナリを生成します。ランタイムはすでにバイナリに埋め込まれているため、コンテナーサイズを最小限に抑えるには runtime-deps ベースイメージを使用してください。
# Build stage
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
WORKDIR /src
COPY . .
RUN dotnet publish -c Release -r linux-x64 -o /app/publish
# Runtime stage -- use runtime-deps, not aspnet or runtime
FROM mcr.microsoft.com/dotnet/runtime-deps:10.0-noble-chiseled AS runtime
WORKDIR /app
COPY --from=build /app/publish .
ENTRYPOINT ["./MyApp"]
runtime-deps イメージには、OS レベルの依存関係(libc、OpenSSL など)のみが含まれ、.NET ランタイムは含まれません。AOT で発行したアプリに使える、最小サイズのベースイメージです(約 30 MB)。コンテナーのパターン全般については、[skill:dotnet-containers] を参照してください。
ASP.NET Core Native AOT
Minimal API のサポート(.NET 8 以降)
ASP.NET Core Minimal API は Native AOT をサポートしています。MVC コントローラーは、モデルバインディング、フィルター、ルーティングでリフレクションに依存するため、AOT 互換ではありません。
var builder = WebApplication.CreateSlimBuilder(args);
// Use source-generated JSON context
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonContext.Default);
});
var app = builder.Build();
app.MapGet("/api/products/{id}", (int id) =>
Results.Ok(new Product(id, "Widget")));
app.Run();
[JsonSerializable(typeof(Product))]
internal partial class AppJsonContext : JsonSerializerContext { }
record Product(int Id, string Name);
CreateSlimBuilder と CreateBuilder の比較
| メソッド | AOT サポート | 含まれる機能 |
|---|---|---|
WebApplication.CreateSlimBuilder() | 完全 | 最小限のサービス。MVC と Razor は含まれません |
WebApplication.CreateBuilder() | 部分的 | すべての機能。一部の機能ではリフレクションが必要です |
Native AOT アプリケーションでは CreateSlimBuilder を使用してください。実行時コード生成が必要な機能は含まれません。
.NET 10 ASP.NET Core AOT の改善
.NET 10 では、ASP.NET Core とランタイムの Native AOT スタック全体に改善が加えられています。net10.0 をターゲットにすると、自動的にこれらの恩恵を受けられます。
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<PublishAot>true</PublishAot>
</PropertyGroup>
Request Delegate Generator の改善: Minimal API エンドポイントのリクエストデリゲートを生成するソースジェネレーターは、.NET 10 でより多くのパラメーターのバインドシナリオに対応しています。これには、追加の TypedResults 戻り値の型や複雑なバインドパターンが含まれます。これにより、特定のエンドポイントシグネチャに対してジェネレーターが AOT セーフなコードを生成できなかった .NET 8/9 で必要だった、手動の回避策を減らせます。
リンカー警告の対象範囲の縮小: 以前はトリミング/AOT 警告(IL2xxx/IL3xxx)を出力していた多くの ASP.NET Core フレームワーク API に、AOT 互換性のための注釈が追加されたか、リファクタリングが行われました。.NET 9 から .NET 10 にアップグレードしたプロジェクトでは、PublishAot を指定して発行する際に、誤検知によるリンカー警告が減ります。
webapiaot テンプレートでの OpenAPI: webapiaot プロジェクトテンプレートには、Microsoft.AspNetCore.OpenApi による OpenAPI ドキュメントの生成が既定で含まれるようになりました。そのため、AOT で発行した API は追加の設定なしで API ドキュメントを自動生成できます。
ランタイムの NativeAOT コード生成: .NET 10 ランタイムでは、構造体引数に対する AOT コード生成が改善され、ループ反転の最適化が強化され、メソッドの非仮想化が改善されています。これにより、コードを変更しなくても、AOT で発行したアプリケーションのスループットが向上します。
Blazor Server と SignalR: Blazor Server と SignalR は、.NET 10 でも Native AOT ではサポートされていません。Blazor WebAssembly AOT(クライアント側のコンパイル)は別の項目で、[skill:dotnet-aot-wasm] で説明されています。Blazor Server アプリでは、引き続き JIT デプロイを使用してください。
互換性の概要(.NET 10):
| 機能 | AOT サポート |
|---|---|
| gRPC | 完全にサポート |
| Minimal APIs | 部分的にサポート(ほとんどのシナリオで動作) |
| MVC | サポート対象外 |
| Blazor Server | サポート対象外 |
| SignalR | サポート対象外 |
| JWT Authentication | 完全にサポート |
| CORS, HealthChecks, OutputCaching | 完全にサポート |
| WebSockets, StaticFiles | 完全にサポート |
エージェントが注意すべき点
- ライブラリプロジェクトで
PublishAotを使用しないでください。 ライブラリではIsAotCompatibleを使用します(AOT アナライザーが自動的に有効になります)。PublishAotは、スタンドアロン実行可能ファイルを生成するアプリケーション向けです。 - 型の保持にレガシー RD.xml を使用しないでください。 RD.xml は .NET Native/UWP の形式であり、最新の .NET AOT では警告なしに無視されます。代わりに ILLink ディスクリプター XML ファイルと
[DynamicDependency]属性を使用してください。 - 新しい AOT コードで
[DllImport]を使用しないでください。 コンパイル時にマーシャリングコードを生成する[LibraryImport](.NET 7 以降)を使用してください。[DllImport]では、AOT で利用できないランタイムマーシャリングが必要になる場合があります。 - AOT API で
WebApplication.CreateBuilder()を使用しないでください。 リフレクションを多用する機能を除外するCreateSlimBuilder()を使用してください。CreateBuilder()には AOT と互換性のない MVC インフラストラクチャが含まれています。 - 分析に
dotnet publish --no-actual-publishを使用しないでください。 このフラグは存在しません。発行せずに診断警告を取得するには、dotnet build /p:EnableAotAnalyzer=true /p:EnableTrimAnalyzer=trueを使用してください。 - MVC コントローラーが Native AOT で動作すると想定しないでください。 MVC はモデルバインド、アクションフィルター、ルーティングにリフレクションを使用します。AOT で発行する Web アプリケーションには Minimal APIs を使用してください。