次の方法で共有


チュートリアル: XML ドキュメントを作成する

このチュートリアルでは、既存のオブジェクト指向サンプル (前の チュートリアルから) を取得し、XML ドキュメントコメントを使用して拡張します。 XML ドキュメントのコメントは、役に立つ IntelliSense ヒントを提供し、生成された API リファレンス ドキュメントに参加できます。コメントに値する要素、 <summary><param><returns><value><remarks><example><seealso><exception><inheritdoc>などのコア タグを使用する方法、および一貫性のある意図的なコメントによって保守性、発見可能性、コラボレーションがノイズを追加することなくどのように向上するかを学習します。 最後に、サンプルのパブリック サーフェスに注釈を付け、XML ドキュメント ファイルを出力するプロジェクトをビルドし、それらのコメントが開発者エクスペリエンスとダウンストリーム ドキュメント ツールにどのように直接送られるかを確認しました。

このチュートリアルでは、次の操作を行います。

  • C# プロジェクトで XML ドキュメント出力を有効にします。
  • XML ドキュメント コメントを型とメンバーに追加および構造化します。
  • プロジェクトをビルドし、生成された XML ドキュメント ファイルを調べます。

[前提条件]

XML ドキュメントを有効にする

前の オブジェクト指向チュートリアルでビルドしたプロジェクトを読み込みます。 新たに開始する場合は、dotnet/docs フォルダーの下にあるsnippets/object-oriented-programming リポジトリからサンプルを複製します。

次に、XML ドキュメント出力を有効にして、コンパイラがアセンブリと共に .xml ファイルを出力します。 プロジェクト ファイルを編集し、 <PropertyGroup> 要素内に次のプロパティを追加 (または確認) します。

<GenerateDocumentationFile>True</GenerateDocumentationFile>

Visual Studio を使用している場合は、"build" プロパティ ページを使用してこれを有効にすることができます。

プロジェクトをビルドします。 コンパイラは、パブリックに表示される型とメンバーからのすべての /// コメントを集計する XML ファイルを生成します。 このファイルは、IntelliSense のヒント、静的分析ツール、およびダウンストリーム ドキュメント生成システムにフィードされます。

プロジェクトを今すぐビルドします。 <summary> コメントがないパブリックメンバーに対する警告を確認できます。 これらの警告は、完全で意図的なドキュメントを提供するのに役立つ to-do リストとして扱います。 生成された XML ファイル (ビルド出力の横) を開き、初期構造を調べます。 最初は、コメントをまだ追加していないため、 <members> セクションは空です。

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>oo-programming</name>
    </assembly>
    <members>
    </members>
</doc>

ファイルを配置したら、対象の XML コメントの追加を開始し、生成された出力にそれぞれがどのように表示されるかをすぐに確認します。 Transaction レコードの種類から始めます。

namespace OOProgramming;

/// <summary>
/// Represents an immutable financial transaction with an amount, date, and descriptive notes.
/// </summary>
/// <param name="Amount">The transaction amount. Positive values represent credits/deposits, negative values represent debits/withdrawals.</param>
/// <param name="Date">The date and time when the transaction occurred.</param>
/// <param name="Notes">Descriptive notes or memo text associated with the transaction.</param>
public record Transaction(decimal Amount, DateTime Date, string Notes);

ドキュメントコメントを追加する

ここで、ビルド警告を順番に実行して、 BankAccount の種類に簡潔で便利なドキュメントを追加します。 各警告は、 <summary> (またはその他の必須) 要素がないパブリック メンバーを特定します。 警告リストをチェックリストとして扱います。 ノイズの追加を避ける: 意図、不変条件、および重要な使用の制約の記述に重点を置きます。明らかな型名またはパラメーター型の記述はスキップしてください。

  1. プロジェクトをもう一度ビルドします。 Visual Studio または Visual Studio Code で、[エラー一覧] または [問題] パネルを開き、ドキュメントの警告をフィルター処理します (CS1591)。 コマンド ラインでビルドを実行し、コンソールに出力された警告を確認します。
  2. 最初の警告 ( BankAccount クラス) に移動します。 宣言の上の行に「 ///」と入力します。 エディターは、 <summary> 要素をスキャフォールディングします。 プレースホルダーを、アクションに重点を置いた 1 つの文に置き換えます。 この文では、ドメイン内のアカウントの役割について説明します。 たとえば、トランザクションを追跡し、最小残高を適用します。
  3. 動作を説明する必要がある場合にのみ、 <remarks> を追加します。 たとえば、最小残高の適用のしくみや、アカウント番号の生成方法などがあります。 備考は短くしてください。
  4. プロパティ (NumberOwnerBalance) ごとに、「 /// 」と入力し、値が何を表しているかを示す <summary> を記述します。これは、単純なゲッターが返す方法ではありません。 プロパティが値 ( Balance など) を計算する場合は、計算を明確にする <value> 要素を追加します。
  5. 各コンストラクターに、パラメーター名を再指定するだけでなく、 <summary> と各引数の意味を説明する <param> 要素を追加します。 あるオーバーロードが別のオーバーロードに委任する場合は、簡潔な<remarks>要素を追加します。
  6. スローできるメソッドの場合は、意図的な例外の種類ごとに <exception> タグを追加します。 トリガーする条件について説明します。 引数検証ヘルパーによってスローされる例外は、パブリック コントラクトの一部でない限り文書化しないでください。
  7. 値を返すメソッドの場合は、呼び出し元が受け取る内容の簡単な説明を含む <returns> を追加します。 メソッド名またはマネージド型を繰り返さないようにします。
  8. 最初に BankAccount 基底クラスを操作します。

あなたのバージョンは次のコードのようなものになります。

namespace OOProgramming;

/// <summary>
/// Represents a bank account with basic banking operations including deposits, withdrawals, and transaction history.
/// Supports minimum balance constraints and provides extensible month-end processing capabilities.
/// </summary>
public class BankAccount
{
    /// <summary>
    /// Gets the unique account number for this bank account.
    /// </summary>
    /// <value>A string representation of the account number, generated sequentially.</value>
    public string Number { get; }
    
    /// <summary>
    /// Gets or sets the name of the account owner.
    /// </summary>
    /// <value>The full name of the person who owns this account.</value>
    public string Owner { get; set; }
    
    /// <summary>
    /// Gets the current balance of the account by calculating the sum of all transactions.
    /// </summary>
    /// <value>The current account balance as a decimal value.</value>
    public decimal Balance => _allTransactions.Sum(i => i.Amount);

    private static int s_accountNumberSeed = 1234567890;

    private readonly decimal _minimumBalance;

    /// <summary>
    /// Initializes a new instance of the BankAccount class with the specified owner name and initial balance.
    /// Uses a default minimum balance of 0.
    /// </summary>
    /// <param name="name">The name of the account owner.</param>
    /// <param name="initialBalance">The initial deposit amount for the account.</param>
    /// <remarks>
    /// This constructor is a convenience overload that calls the main constructor with a minimum balance of 0.
    /// If the initial balance is greater than 0, it will be recorded as the first transaction with the note "Initial balance".
    /// The account number is automatically generated using a static seed value that increments for each new account.
    /// </remarks>
    public BankAccount(string name, decimal initialBalance) : this(name, initialBalance, 0) { }

    /// <summary>
    /// Initializes a new instance of the BankAccount class with the specified owner name, initial balance, and minimum balance constraint.
    /// </summary>
    /// <param name="name">The name of the account owner.</param>
    /// <param name="initialBalance">The initial deposit amount for the account.</param>
    /// <param name="minimumBalance">The minimum balance that must be maintained in the account.</param>
    /// <remarks>
    /// This is the primary constructor that sets up all account properties. The account number is generated automatically
    /// using a static seed value. If an initial balance is provided and is greater than 0, it will be added as the first
    /// transaction. The minimum balance constraint will be enforced on all future withdrawal operations through the
    /// <see cref="CheckWithdrawalLimit"/> method.
    /// </remarks>
    public BankAccount(string name, decimal initialBalance, decimal minimumBalance)
    {
        Number = s_accountNumberSeed.ToString();
        s_accountNumberSeed++;

        Owner = name;
        _minimumBalance = minimumBalance;
        if (initialBalance > 0)
            MakeDeposit(initialBalance, DateTime.Now, "Initial balance");
    }

    private readonly List<Transaction> _allTransactions = [];

    /// <summary>
    /// Makes a deposit to the account by adding a positive transaction.
    /// </summary>
    /// <param name="amount">The amount to deposit. Must be positive.</param>
    /// <param name="date">The date when the deposit is made.</param>
    /// <param name="note">A descriptive note about the deposit transaction.</param>
    /// <exception cref="ArgumentOutOfRangeException">Thrown when the deposit amount is zero or negative.</exception>
    /// <remarks>
    /// This method creates a new <see cref="Transaction"/> object with the specified amount, date, and note,
    /// then adds it to the internal transaction list. The transaction amount must be positive - negative amounts
    /// are not allowed for deposits. The account balance is automatically updated through the Balance property
    /// which calculates the sum of all transactions. There are no limits or restrictions on deposit amounts.
    /// </remarks>
    public void MakeDeposit(decimal amount, DateTime date, string note)
    {
        if (amount <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(amount), "Amount of deposit must be positive");
        }
        var deposit = new Transaction(amount, date, note);
        _allTransactions.Add(deposit);
    }

    /// <summary>
    /// Makes a withdrawal from the account by adding a negative transaction.
    /// Checks withdrawal limits and minimum balance constraints before processing.
    /// </summary>
    /// <param name="amount">The amount to withdraw. Must be positive.</param>
    /// <param name="date">The date when the withdrawal is made.</param>
    /// <param name="note">A descriptive note about the withdrawal transaction.</param>
    /// <exception cref="ArgumentOutOfRangeException">Thrown when the withdrawal amount is zero or negative.</exception>
    /// <exception cref="InvalidOperationException">Thrown when the withdrawal would cause the balance to fall below the minimum balance.</exception>
    /// <remarks>
    /// This method first validates that the withdrawal amount is positive, then checks if the withdrawal would
    /// violate the minimum balance constraint by calling <see cref="CheckWithdrawalLimit"/>. The withdrawal is
    /// recorded as a negative transaction amount. If the withdrawal limit check returns an overdraft transaction
    /// (such as a fee), that transaction is also added to the account. The method enforces business rules through
    /// the virtual CheckWithdrawalLimit method, allowing derived classes to implement different withdrawal policies.
    /// </remarks>
    public void MakeWithdrawal(decimal amount, DateTime date, string note)
    {
        if (amount <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive");
        }
        Transaction? overdraftTransaction = CheckWithdrawalLimit(Balance - amount < _minimumBalance);
        Transaction? withdrawal = new(-amount, date, note);
        _allTransactions.Add(withdrawal);
        if (overdraftTransaction != null)
            _allTransactions.Add(overdraftTransaction);
    }

    /// <summary>
    /// Checks whether a withdrawal would violate the account's minimum balance constraint.
    /// This method can be overridden in derived classes to implement different withdrawal limit policies.
    /// </summary>
    /// <param name="isOverdrawn">True if the withdrawal would cause the balance to fall below the minimum balance.</param>
    /// <returns>A Transaction object representing any overdraft fees or penalties, or null if no additional charges apply.</returns>
    /// <exception cref="InvalidOperationException">Thrown when the withdrawal would cause an overdraft and the account type doesn't allow it.</exception>
    protected virtual Transaction? CheckWithdrawalLimit(bool isOverdrawn)
    {
        if (isOverdrawn)
        {
            throw new InvalidOperationException("Not sufficient funds for this withdrawal");
        }
        else
        {
            return default;
        }
    }

    /// <summary>
    /// Generates a detailed account history report showing all transactions with running balance calculations.
    /// </summary>
    /// <returns>A formatted string containing the complete transaction history with dates, amounts, running balances, and notes.</returns>
    /// <remarks>
    /// This method creates a formatted report that includes a header row followed by all transactions in chronological order.
    /// Each row shows the transaction date (in short date format), the transaction amount, the running balance after that
    /// transaction, and any notes associated with the transaction. The running balance is calculated by iterating through
    /// all transactions and maintaining a cumulative total. The report uses tab characters for column separation and
    /// is suitable for display in console applications or simple text outputs.
    /// </remarks>
    public string GetAccountHistory()
    {
        var report = new System.Text.StringBuilder();

        decimal balance = 0;
        report.AppendLine("Date\t\tAmount\tBalance\tNote");
        foreach (var item in _allTransactions)
        {
            balance += item.Amount;
            report.AppendLine($"{item.Date.ToShortDateString()}\t{item.Amount}\t{balance}\t{item.Notes}");
        }

        return report.ToString();
    }

    /// <summary>
    /// Performs month-end processing for the account. This virtual method can be overridden in derived classes
    /// to implement specific month-end behaviors such as interest calculations, fee assessments, or statement generation.
    /// </summary>
    /// <remarks>
    /// The base implementation of this method does nothing, providing a safe default for basic bank accounts.
    /// Derived classes such as savings accounts or checking accounts can override this method to implement
    /// account-specific month-end processing. Examples include calculating and applying interest payments,
    /// assessing monthly maintenance fees, generating account statements, or performing regulatory compliance checks.
    /// This method is typically called by banking systems at the end of each month as part of batch processing operations.
    /// </remarks>
    public virtual void PerformMonthEndTransactions() { }
}

完了したら、再生成された XML ファイルを開き、各メンバーが新しい要素と共に表示されることを確認します。 トリミングされた部分は次のようになります。

<member name="T:OOProgramming.BankAccount">
  <summary>Represents a bank account that records transactions and enforces an optional minimum balance.</summary>
  <remarks>Account numbers are generated sequentially when each instance is constructed.</remarks>
</member>
<member name="P:OOProgramming.BankAccount.Balance">
  <summary>Gets the current balance based on all recorded transactions.</summary>
  <value>The net sum of deposits and withdrawals.</value>
</member>

ヒント

概要を 1 つの文に保持します。 複数のコンテキストが必要な場合は、セカンダリ コンテキストを <remarks>に移動します。

派生クラスで <inheritdoc/> を使用する

BankAccountから派生した場合(たとえば、利息を計算するSavingsAccountの場合)、ドキュメントをコピーする代わりに基本のものを継承できます。 派生メンバーのドキュメント ブロック内に自己終了 <inheritdoc/> 要素を追加します。 特殊な動作を文書化するために、<remarks>の後にさらに多くの要素 (余分な<inheritdoc/>の詳細など) を追加することもできます。

/// <inheritdoc/>
/// <remarks>
/// An interest-earning account is a specialized savings account that rewards customers for maintaining higher balances.
/// Interest is only earned when the account balance exceeds $500, encouraging customers to maintain substantial deposits.
/// The annual interest rate of 2% is applied monthly to qualifying balances, providing a simple savings incentive.
/// This account type uses the standard minimum balance of $0 from the base <see cref="BankAccount"/> class.
/// </remarks>
public class InterestEarningAccount : BankAccount

<inheritdoc/> は重複を減らし、基本型のドキュメントを後で更新するときに一貫性を維持するのに役立ちます。

パブリック サーフェスの文書化が完了したら、最後に 1 回ビルドして、CS1591 の警告が残っていないか確認します。 これで、プロジェクトで便利な IntelliSense と、ワークフローを発行するための準備が整った構造化 XML ファイルが生成されます。

GitHub の dotnet/docs リポジトリのソース フォルダーで、注釈付きの完全なサンプルを確認できます。

コメントからの出力をビルドする

XML コメントからの出力を作成するには、次のいずれかのツールを試してさらに詳しく調べることができます。

  • DocFX: DocFX は、現在 C#、Visual Basic、F# をサポートしている .NET 用の API ドキュメント ジェネレーターです。 また、生成された参照ドキュメントをカスタマイズすることもできます。 DocFX は、ソース コードと Markdown ファイルから静的 HTML Web サイトを構築します。 また、DocFX では、テンプレートを使用して Web サイトのレイアウトとスタイルを柔軟にカスタマイズできます。 カスタム テンプレートを作成することもできます。
  • Sandcastle: Sandcastle ツール は、概念と API の両方のリファレンス ページを含むマネージド クラス ライブラリ用のヘルプ ファイルを作成します。 Sandcastle ツールはコマンドライン ベースであり、GUI フロントエンド、プロジェクト管理機能、または自動ビルド プロセスはありません。 Sandcastle ヘルプ ファイル ビルダーには、自動化された方法でヘルプ ファイルを作成するためのスタンドアロンの GUI とコマンド ライン ベースのツールが用意されています。 Visual Studio 統合パッケージも使用できるため、Visual Studio 内からヘルプ プロジェクトを完全に作成および管理できます。
  • Doxygen: Doxygen は、ドキュメント化された一連のソース ファイルからオンライン ドキュメント ブラウザー (HTML) またはオフライン参照マニュアル (LaTeX) を生成します。 RTF (MS Word)、PostScript、ハイパーリンク PDF、圧縮 HTML、DocBook、Unix のマニュアル ページで出力を生成することもできます。 文書化されていないソース ファイルからコード構造を抽出するように Doxygen を構成できます。