dotnet-xml-docs について
XML ドキュメントコメントを記述する .NET ライブラリ開発者向けのスキルです。タグ構文や記述規約、MSBuild の生成設定、警告対応、IDE の補完と生成ツールを扱います。
主な機能
- 01標準・高度な XML タグ
- 02XML ドキュメント出力の有効化
- 03CS1591 警告への対応
- 04公開 API のドキュメント規約
- 05IDE でのコメント骨格生成
- 06IntelliSense でのドキュメント表示
活用シーン
- 01公開 NuGet API のコメントを作成
- 02ビルド時の XML ファイル生成を設定
- 03ドキュメント不足のコンパイラー警告を管理
- 04IDE でコメントを生成して改善
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-xml-docsインストール後、Claude Code で/dotnet-xml-docsを入力して読み込まれたか確認します。
詳細
dotnet-xml-docs
.NET の XML ドキュメントコメント:すべての標準タグ(<summary>、<param>、<returns>、<exception>、<remarks>、<example>、<value>、<typeparam>、<typeparamref>、<paramref>)、高度なタグ(インターフェースと基底クラスの継承に使う <inheritdoc>、<see cref="..."/>、<seealso>、<c>、<code>)、<GenerateDocumentationFile> MSBuild プロパティによる XML ドキュメント生成の有効化、内部 API の警告を抑制する方法(CS1591、<NoWarn>、InternalsVisibleTo)、公開 NuGet ライブラリ向けの XML ドキュメント規約、自動生成ツール(IDE のクイックフィックスを起動する ///、GhostDoc 形式のパターン)、XML ドキュメントを IDE のツールチップや自動補完に表示する IntelliSense との連携。
バージョンの前提: .NET 8.0 以降を基準とします。XML ドキュメントコメントは、すべての .NET バージョンで利用できる C# 言語機能です。<GenerateDocumentationFile> MSBuild プロパティは .NET SDK 6 以降で動作します。<inheritdoc> は C# 9.0/.NET 5 以降で完全にサポートされています。
対象範囲: このスキルの対象は XML ドキュメントコメントの作成です。つまり、XML ドキュメントファイルを生成するための構文、規約、MSBuild の構成を扱います。XML コメントからの API ドキュメントサイト生成(DocFX、Starlight)は [skill:dotnet-api-docs] の対象です。一般的な C# コーディング規約(命名、書式設定)は [skill:dotnet-csharp-coding-standards] の対象です。
対象外: XML コメントからの API ドキュメントサイト生成(DocFX の設定、ドキュメントとしての OpenAPI、ドキュメントとコードの同期)については、[skill:dotnet-api-docs] を参照してください。一般的な C# コーディング規約と命名規則については、[skill:dotnet-csharp-coding-standards] を参照してください。ドキュメントサイトの CI/CD デプロイについては、[skill:dotnet-gha-deploy] を参照してください。
相互参照:[skill:dotnet-api-docs] は XML コメントからの API ドキュメント生成、[skill:dotnet-csharp-coding-standards] は一般的な C# コーディング規約、[skill:dotnet-gha-deploy] はドキュメントサイトのデプロイを扱います。
XML ドキュメント生成の有効化
MSBuild の構成
プロジェクトまたは Directory.Build.props で XML ドキュメントファイルの生成を有効にします。
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
これにより、ビルド時にアセンブリと同じ場所に .xml ファイルが生成されます(例:MyLibrary.dll の隣に MyLibrary.xml)。NuGet パックではこの XML ファイルが自動的にパッケージに含まれ、パッケージ利用者が IntelliSense を使えるようになります。
内部 API の警告の抑制
GenerateDocumentationFile を有効にすると、XML ドキュメントコメントがないすべての公開メンバーに対して、コンパイラーが CS1591 警告を出します。内部向けコードについては、選択的に警告を抑制します。
方法 1:プロジェクト全体で一括して抑制する(公開ライブラリでは非推奨):
<PropertyGroup>
<NoWarn>$(NoWarn);CS1591</NoWarn>
</PropertyGroup>
方法 2:プラグマディレクティブでファイルごとに抑制する(公開範囲が混在するアセンブリに推奨):
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
public class InternalServiceHelper
{
// This type is internal-facing despite being public
// (e.g., exposed for testing via InternalsVisibleTo)
}
#pragma warning restore CS1591
方法 3:InternalsVisibleTo を使い、内部型を内部のままにする:
// In AssemblyInfo.cs or a Properties file
[assembly: InternalsVisibleTo("MyLibrary.Tests")]
// Mark internal-facing types as internal instead of public
internal class ServiceHelper
{
// No CS1591 warning -- internal types are not documented
}
方法 4:公開ライブラリではドキュメントの不足をエラーとして扱う(最も厳格):
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<WarningsAsErrors>$(WarningsAsErrors);CS1591</WarningsAsErrors>
</PropertyGroup>
これにより、すべての公開メンバーにドキュメントが必須になります。利用者が IntelliSense のドキュメントに依存する NuGet パッケージで使用してください。
標準の XML ドキュメントタグ
<summary>
型またはメンバーについて説明します。IntelliSense のツールチップに表示される主要なタグです。
/// <summary>
/// Provides methods for managing widgets in the system.
/// Widget operations are thread-safe and support cancellation.
/// </summary>
public class WidgetService
{
}
<param>
メソッドのパラメーターについて説明します。
/// <summary>
/// Creates a new widget with the specified name and optional category.
/// </summary>
/// <param name="name">The display name for the widget. Must not be null or whitespace.</param>
/// <param name="category">
/// The category to assign. When <see langword="null"/>, the default category is used.
/// </param>
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
public async Task<Widget> CreateWidgetAsync(
string name,
string? category = null,
CancellationToken cancellationToken = default)
{
}
<returns>
戻り値について説明します。
/// <summary>
/// Finds a widget by its unique identifier.
/// </summary>
/// <param name="id">The unique identifier of the widget.</param>
/// <returns>
/// The widget if found; otherwise, <see langword="null"/>.
/// </returns>
public async Task<Widget?> FindByIdAsync(Guid id)
{
}
<exception>
スローされる可能性のある例外について説明します。
/// <summary>
/// Updates the widget's name.
/// </summary>
/// <param name="id">The widget identifier.</param>
/// <param name="newName">The new name to assign.</param>
/// <exception cref="ArgumentException">
/// Thrown when <paramref name="newName"/> is null or whitespace.
/// </exception>
/// <exception cref="KeyNotFoundException">
/// Thrown when no widget with the specified <paramref name="id"/> exists.
/// </exception>
/// <exception cref="InvalidOperationException">
/// Thrown when the widget is in a read-only state and cannot be modified.
/// </exception>
public async Task UpdateNameAsync(Guid id, string newName)
{
}
<remarks>
概要に加えて、補足情報を記述します。実装上の注意点、使用方法のガイダンス、留意事項に使います。
/// <summary>
/// Computes the hash of the widget's content for change detection.
/// </summary>
/// <remarks>
/// <para>
/// The hash is computed using SHA-256 over the UTF-8 encoded content.
/// Results are deterministic across platforms and .NET versions.
/// </para>
/// <para>
/// This method is thread-safe. The returned hash is a lowercase hex string
/// without any prefix (e.g., "a1b2c3..." not "0xa1b2c3...").
/// </para>
/// </remarks>
/// <returns>A 64-character lowercase hexadecimal hash string.</returns>
public string ComputeContentHash()
{
}
<example>
使い方を示すコード例を記述します。公開ライブラリの API では不可欠です。
/// <summary>
/// Creates a new widget and persists it to the repository.
/// </summary>
/// <example>
/// <code>
/// var service = new WidgetService(repository, logger);
///
/// var widget = await service.CreateWidgetAsync(
/// "Dashboard Widget",
/// category: "Analytics",
/// cancellationToken: ct);
///
/// Console.WriteLine($"Created widget: {widget.Id}");
/// </code>
/// </example>
public async Task<Widget> CreateWidgetAsync(
string name,
string? category = null,
CancellationToken cancellationToken = default)
{
}
<value>
プロパティの値について説明します。<returns> と同様ですが、プロパティに使用します。
/// <summary>
/// Gets the current status of the widget.
/// </summary>
/// <value>
/// The widget status. Defaults to <see cref="WidgetStatus.Draft"/> for new widgets.
/// </value>
public WidgetStatus Status { get; private set; }
<typeparam>
ジェネリック型パラメーターについて説明します。
/// <summary>
/// A repository that provides CRUD operations for entities.
/// </summary>
/// <typeparam name="TEntity">
/// The entity type. Must implement <see cref="IEntity"/> and have a parameterless constructor.
/// </typeparam>
/// <typeparam name="TKey">
/// The type of the entity's primary key. Typically <see cref="Guid"/> or <see cref="int"/>.
/// </typeparam>
public interface IRepository<TEntity, TKey>
where TEntity : class, IEntity
where TKey : struct
{
}
高度なタグ
<inheritdoc>
基底クラスまたはインターフェイスからドキュメントを継承します。インターフェイスの実装や仮想メンバーのオーバーライド時の重複をなくします。
インターフェイスの実装:
public interface IWidgetService
{
/// <summary>
/// Creates a new widget with the specified name.
/// </summary>
/// <param name="name">The display name for the widget.</param>
/// <returns>The created widget.</returns>
Task<Widget> CreateWidgetAsync(string name);
}
public class WidgetService : IWidgetService
{
/// <inheritdoc />
public async Task<Widget> CreateWidgetAsync(string name)
{
// Implementation inherits all documentation from the interface
}
}
基底クラスのオーバーライド:
public abstract class RepositoryBase<T>
{
/// <summary>
/// Retrieves an entity by its unique identifier.
/// </summary>
/// <param name="id">The entity identifier.</param>
/// <returns>The entity if found; otherwise, <see langword="null"/>.</returns>
public abstract Task<T?> GetByIdAsync(Guid id);
}
public class OrderRepository : RepositoryBase<Order>
{
/// <inheritdoc />
public override async Task<Order?> GetByIdAsync(Guid id)
{
// Inherits summary, param, and returns docs from base class
}
}
cref を使った選択的な継承:
/// <inheritdoc cref="IWidgetService.CreateWidgetAsync(string)"/>
/// <remarks>
/// This implementation validates the name and assigns a default category
/// before persisting to the database.
/// </remarks>
public async Task<Widget> CreateWidgetAsync(string name)
{
}
パスフィルターを使った継承:
/// <inheritdoc cref="IWidgetService.CreateWidgetAsync(string)" path="/summary"/>
/// <param name="name">Custom param doc that overrides the interface.</param>
public async Task<Widget> CreateWidgetAsync(string name)
{
}
<see> と <seealso>
インライン参照と「関連項目」リンクを作成します。
/// <summary>
/// Validates the widget against the rules defined in <see cref="WidgetValidator"/>.
/// Use <see cref="WidgetService.CreateWidgetAsync(string)"/> to create validated widgets.
/// </summary>
/// <seealso cref="WidgetValidator"/>
/// <seealso cref="IValidationResult"/>
/// <seealso href="https://docs.mylib.dev/validation">Validation Guide</seealso>
public ValidationResult Validate(Widget widget)
{
}
<see> のバリエーション:
/// <summary>
/// Returns <see langword="true"/> if the widget is active,
/// <see langword="false"/> otherwise.
/// Use <see cref="Activate"/> to change the state.
/// The value is stored as a <see cref="bool"/>.
/// </summary>
public bool IsActive { get; }
<c> と <code>
ドキュメント内のインラインコードとコードブロックを整形します。
/// <summary>
/// Parses a widget name from the format <c>"category:name"</c>.
/// </summary>
/// <remarks>
/// The expected input format:
/// <code>
/// "analytics:page-views" // category = "analytics", name = "page-views"
/// "default:my-widget" // category = "default", name = "my-widget"
/// "my-widget" // category = null, name = "my-widget" (no colon)
/// </code>
/// </remarks>
public (string? Category, string Name) ParseWidgetName(string input)
{
}
<paramref> と <typeparamref>
ドキュメントのテキスト内で、パラメーターと型パラメーターを参照します。
/// <summary>
/// Converts <paramref name="entity"/> to a DTO of type <typeparamref name="TDto"/>.
/// If <paramref name="entity"/> is <see langword="null"/>, returns the default value
/// of <typeparamref name="TDto"/>.
/// </summary>
/// <typeparam name="TDto">The target DTO type.</typeparam>
/// <param name="entity">The source entity to convert.</param>
public TDto? ToDto<TDto>(object? entity)
{
}
<list>
ドキュメント内に箇条書き、番号付きリスト、または表を作成します。
/// <summary>
/// Applies the specified transformation to the widget.
/// </summary>
/// <remarks>
/// Supported transformations:
/// <list type="bullet">
/// <item><description>Resize -- changes the widget dimensions</description></item>
/// <item><description>Recolor -- applies a new color scheme</description></item>
/// <item><description>Reposition -- moves the widget to new coordinates</description></item>
/// </list>
/// </remarks>
public void ApplyTransformation(Widget widget, Transformation transform)
{
}
XML ドキュメントの包括的な例
すべてのタグが連携して機能する様子を示す完全なクラスの例:
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace MyLibrary;
/// <summary>
/// Manages the lifecycle of widgets including creation, retrieval, update, and deletion.
/// All operations are thread-safe and support cancellation via <see cref="CancellationToken"/>.
/// </summary>
/// <remarks>
/// <para>
/// <see cref="WidgetService"/> requires a registered <see cref="IWidgetRepository"/>
/// and <see cref="ILogger{TCategoryName}"/> via dependency injection.
/// </para>
/// <para>
/// Widget names must be unique within a category. Attempting to create a widget with a
/// duplicate name in the same category throws <see cref="InvalidOperationException"/>.
/// </para>
/// </remarks>
/// <example>
/// Register and use the service:
/// <code>
/// // Registration
/// builder.Services.AddScoped<IWidgetService, WidgetService>();
///
/// // Usage
/// var widget = await widgetService.CreateWidgetAsync(
/// "My Widget",
/// category: "Dashboard");
/// </code>
/// </example>
/// <seealso cref="IWidgetRepository"/>
/// <seealso cref="Widget"/>
public class WidgetService : IWidgetService
{
private readonly IWidgetRepository _repository;
private readonly ILogger<WidgetService> _logger;
/// <summary>
/// Initializes a new instance of <see cref="WidgetService"/>.
/// </summary>
/// <param name="repository">The widget data repository.</param>
/// <param name="logger">The logger for diagnostic output.</param>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="repository"/> or <paramref name="logger"/> is
/// <see langword="null"/>.
/// </exception>
public WidgetService(IWidgetRepository repository, ILogger<WidgetService> logger)
{
_repository = repository ?? throw new ArgumentNullException(nameof(repository));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
/// <summary>
/// Gets the total number of widgets in the system.
/// </summary>
/// <value>A non-negative integer representing the total widget count.</value>
public int TotalCount => _repository.Count;
/// <inheritdoc />
public async Task<Widget> CreateWidgetAsync(
string name,
string? category = null,
CancellationToken cancellationToken = default)
{
ArgumentException.ThrowIfNullOrWhiteSpace(name);
_logger.LogInformation("Creating widget {Name} in category {Category}", name, category);
var widget = new Widget
{
Id = Guid.NewGuid(),
Name = name,
Category = category ?? "default",
CreatedAt = DateTimeOffset.UtcNow,
Status = WidgetStatus.Draft
};
await _repository.AddAsync(widget, cancellationToken);
return widget;
}
/// <summary>
/// Retrieves all widgets matching the specified filter criteria.
/// </summary>
/// <param name="filter">
/// The filter to apply. Use <see cref="WidgetFilter.None"/> for unfiltered results.
/// </param>
/// <param name="cancellationToken">A token to cancel the operation.</param>
/// <returns>
/// A read-only list of widgets matching the filter. Returns an empty list if no
/// widgets match the criteria.
/// </returns>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="filter"/> is <see langword="null"/>.
/// </exception>
public async Task<IReadOnlyList<Widget>> ListAsync(
WidgetFilter filter,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(filter);
return await _repository.FindAsync(filter, cancellationToken);
}
}
公開ライブラリ向け XML ドキュメントの規約
ドキュメントの要件
ほかの開発者が使用する NuGet パッケージでは、次の規約に従ってください。
- すべての公開型に
<summary>を付ける必要があります -- IntelliSense に役立つ情報を表示するための最低条件です - すべての公開メソッドのパラメーターに
<param>を付ける必要があります -- パラメーターが制御する内容、有効な範囲、null の場合の動作を説明します - 例外をスローする可能性があるすべてのメソッドで、
<exception>を使って例外を記述する必要があります -- 呼び出し元が捕捉すべき例外を把握できるようにします - 戻り値には
<returns>を付ける必要があります -- 戻り値の意味を説明します。特に null 許容の戻り値では重要です - プロパティには
<value>を付ける必要があります -- プロパティが表す内容と既定値を説明します - ジェネリック型パラメーターには
<typeparam>を付ける必要があります -- 制約と想定される使い方を説明します - 複雑な API には
<example>を含める必要があります -- 単純な一行の例ではなく、実際の使用例を示します - 注意事項やスレッド動作には
<remarks>を使用します --<summary>に含めるべきではない重要な背景情報を記述します
執筆スタイル
- 三人称の叙述文を使います:「Gets the widget name」ではなく「Get the widget name」や「This gets the widget name」とします
<summary>は動詞で始めます:「Creates...」「Retrieves...」「Validates...」「Computes...」- 明らかなことを繰り返さないようにします:
Nameという名前のプロパティに「Gets the Name property」と書かないでください - 実装ではなく動作を記述します:「Checks the dictionary and returns the value」ではなく「Returns the cached value if available」とします
- すべての型およびメンバー参照に
<see cref="..."/>を使用します -- IDE のナビゲーションとリファクタリングをサポートします null、true、falseをそのまま書く代わりに、<see langword="null"/>、<see langword="true"/>、<see langword="false"/>を使用します
CancellationToken の規約
CancellationToken パラメーターのドキュメントを標準化します。
/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
この一行の説明で、標準的な CancellationToken cancellationToken = default パターンには十分です。キャンセルの意味を過剰に記述しないでください。呼び出し元はこのパターンを理解しています。
自動生成ツール
IDE のクイックフィックスによる生成
主要な .NET IDE では、メンバーの上に /// と入力すると、XML ドキュメントのひな形が生成されます。
Visual Studio / VS Code (C# Dev Kit):
- メソッド、クラス、またはプロパティの上に
///と入力します - IDE は
<summary>のひな形を生成し、メンバーのシグネチャに基づいて<param>、<returns>、<typeparam>タグをあらかじめ入力します
JetBrains Rider:
///と入力すると、同様のひな形が生成されます- Rider は XML ドキュメントの不足について警告し、追加するためのクイックフィックスも提案します
GhostDoc 風のパターン
GhostDoc などのツールは、命名規則のヒューリスティックを使ってメンバー名からドキュメントのテキストを生成します。
GetWidgetByIdから「Gets the widget by identifier」が生成されますIsActiveから「Gets a value indicating whether this instance is active」が生成されますCreateWidgetAsyncから「Creates the widget asynchronously」が生成されます
こうした自動生成の説明は出発点です。生成されたテキストは必ず見直し、ドメイン固有の背景情報、パラメーターの制約、例外条件、使用例を追加して改善してください。
EditorConfig との統合
.editorconfig を通じて XML ドキュメントの要件を適用します。
# .editorconfig
[*.cs]
# Require XML documentation for public members
dotnet_diagnostic.CS1591.severity = warning
# For public libraries, treat as error:
# dotnet_diagnostic.CS1591.severity = error
IntelliSense との統合
XML ドキュメントが IDE に表示される仕組み
XML ドキュメントコメントは、IntelliSense のツールチップの主要な情報源です。
- ホバーツールチップ: 型またはメンバーにカーソルを合わせたときに
<summary>のテキストを表示します - パラメーターのヒント: メソッド呼び出し中に、パラメーター情報のポップアップで
<param>のテキストを表示します - 補完リスト: オートコンプリートのドロップダウンに
<summary>を表示します - クイック情報: ドキュメントの展開パネルに
<returns>、<exception>、<remarks>を表示します - シグネチャヘルプ:
<param>の説明とともにパラメーター名と型を表示します
NuGet パッケージの IntelliSense
<GenerateDocumentationFile> が有効で、パッケージが NuGet に公開されると、XML ファイルがパッケージに含まれます。利用者はソースにアクセスしなくても、IntelliSense のサポートをすべて利用できます。
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
追加の NuGet パッケージ構成は不要です。GenerateDocumentationFile が true の場合、dotnet pack は XML ドキュメントファイルを自動的に含めます。
Source Link との統合
XML ドキュメントと Source Link を組み合わせることで、最適な開発者エクスペリエンスを実現できます。
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>
XML ドキュメントと Source Link の両方を有効にすると、利用者はカーソルを合わせてドキュメントを確認し、元のソースコードに移動できます。
エージェントが注意すべき点
-
公開ライブラリでは必ず
<GenerateDocumentationFile>を有効にします。有効にしないと、NuGet の利用者は IntelliSense のドキュメントを利用できません。ソリューション内のすべてのプロジェクトに適用するには、Directory.Build.propsに追加してください。 -
インターフェイスの実装とオーバーライドには
<inheritdoc />を使用します。インターフェイスとその実装の間でドキュメントのテキストを重複させないでください。重複があると、メンテナンス時に内容の不整合が生じます。 -
公開 NuGet パッケージで CS1591 をグローバルに抑制しないでください。
<NoWarn>CS1591</NoWarn>によるグローバルな抑制は、ドキュメントが不足しているすべての警告を非表示にします。意図的にドキュメントを記述しない型には、ファイル単位の#pragmaによる抑制を使用するか、内部型を実際にinternalにしてください。 -
型への参照には、型名をそのまま記述するのではなく、すべて
<see cref="..."/>を使用します。<see cref="Widget"/>を使用すると IDE のナビゲーションが有効になり、ビルド時に検証されます。型名をそのまま「Widget」と記述してもリンクされず、型の名前変更後に古い情報のままになる可能性があります。 -
ドキュメントのテキストでは、
nullをそのまま記述するのではなく<see langword="null"/>を使用します。IntelliSense や API ドキュメントサイトで適切に書式設定されます。true、false、その他の C# キーワードにも同じ方法を使用します。 -
<inheritdoc>はデザイン時ではなくビルド時に解決されます。一部の古い IDE バージョンでは、ツールチップに「ドキュメントが見つかりません」と表示されることがあります。生成された XML ファイルと API ドキュメントサイトでは、ドキュメントは正しく解決されます。 -
XML ドキュメントコメントの文章中でジェネリック型の構文を記述する場合は、
<と>を使用する必要があります。ただし、<see cref="..."/>ではジェネリック型が自動的に処理されます。<see cref="List<T>"/>ではなく、<see cref="List{T}"/>(波括弧)を使用してください。 -
<code>ブロックでは、山括弧に<と>を使用します。XML ドキュメントコメントは XML なので、コード例内の<と>はエスケープする必要があります。または、エスケープを避けるために<![CDATA[...]]>を使用してください。 -
XML コメントから API ドキュメントサイトを生成しないでください。API ドキュメントサイトの生成(DocFX、ドキュメントとしての OpenAPI)は [skill:dotnet-api-docs] の対象です。このスキルでは、XML コメントの作成方法のみを扱います。
-
キャンセルトークンは、標準の一文で記述します。すべての
CancellationTokenパラメーターに「非同期操作をキャンセルするためのトークンです。」を使用してください。キャンセルの仕組みを過剰に説明しないでください。