Tutorial: Criar documentação XML

Neste tutorial, você pega um exemplo orientado a objeto existente (do tutorial anterior) e o aprimora com comentários da documentação XML. Os comentários da documentação XML fornecem dicas úteis de ferramentas do IntelliSense e podem participar de documentos de referência de API gerados. Você aprende quais elementos merecem comentários, como usar tags principais como <summary>, <param>, , <returns>, <value><remarks>, <example>, <seealso>, <exception>e , e como comentários consistentes e <inheritdoc>propositais melhoram a capacidade de manutenção, descoberta e colaboração, sem adicionar ruído. No final, você anotou a superfície pública do exemplo, criou o projeto para emitir o arquivo de documentação XML e viu como esses comentários fluem diretamente para a experiência do desenvolvedor e as ferramentas de documentação downstream.

Neste tutorial, você:

  • Habilite a saída de documentação XML em seu projeto C#.
  • Adicione e estruture comentários de documentação XML para tipos e membros.
  • Crie o projeto e inspecione o arquivo de documentação XML gerado.

Pré-requisitos

Habilitar documentação XML

Carregue o projeto criado no tutorial orientado a objeto anterior. Se preferir começar do zero, clone a amostra do dotnet/docs repositório sob a snippets/object-oriented-programming pasta.

Em seguida, habilite a saída da documentação XML para que o compilador emita um .xml arquivo ao lado do assembly. Edite o arquivo de projeto e adicione (ou confirme) a seguinte propriedade dentro de um <PropertyGroup> elemento :

<GenerateDocumentationFile>True</GenerateDocumentationFile>

Se você estiver usando o Visual Studio, poderá habilitá-lo usando a página de propriedades "build".

Construa o projeto. O compilador produz um arquivo XML que agrega todos os /// comentários de tipos e membros publicamente visíveis. Esse arquivo alimenta dicas de ferramentas do IntelliSense, ferramentas de análise estática e sistemas de geração de documentação downstream.

Construa o projeto agora. Você vê avisos para todos os membros públicos que estão sem <summary> comentários. Trate esses avisos como uma lista de tarefas que ajuda a fornecer documentação completa e intencional. Abra o arquivo XML gerado (ele está ao lado da saída da compilação) e inspecione a estrutura inicial. No início, a <members> seção está vazia porque você ainda não adicionou comentários:

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

Com o arquivo no lugar, comece a adicionar comentários XML direcionados e verifique imediatamente como cada um aparece na saída gerada. Comece com o tipo de registo 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);

Adicionar comentários da documentação

Agora você percorre os avisos de compilação para adicionar documentação concisa e útil ao tipo BankAccount. Cada aviso identifica um membro público que não possui um <summary> elemento (ou outro elemento necessário). Trate a lista de avisos como uma lista de verificação. Evite adicionar ruído: concentre-se em descrever intenção, invariantes e restrições de uso importantes — ignore a reafirmação de nomes de tipo ou tipos de parâmetros óbvios.

  1. Construa o projeto novamente. No Visual Studio ou Visual Studio Code, abra o painel Lista de Erros/Problemas e filtre os avisos de documentação (CS1591). Na linha de comando, execute uma compilação e revise os avisos emitidos para o console.
  2. Navegue até o primeiro aviso (a BankAccount classe). Na linha acima da declaração, digite ///. O editor estrutura um elemento <summary>. Substitua o marcador de posição por uma única frase focada na ação. A frase explica o papel da conta no domínio. Por exemplo, ele rastreia transações e impõe um saldo mínimo.
  3. Adicione <remarks> apenas se precisar explicar o comportamento. Os exemplos incluem como funciona a aplicação do saldo mínimo ou como os números de conta são gerados. Mantenha as observações breves.
  4. Para cada propriedade (Number, Owner, Balance), digite /// e escreva um <summary> que indique o que o valor representa — não como um getter trivial o retorna. Se uma propriedade calcular um valor (como Balance), adicione um <value> elemento que esclareça o cálculo.
  5. Para cada construtor, adicione <summary> mais <param> elementos descrevendo o significado de cada argumento, não apenas reafirmando o nome do parâmetro. Se uma sobrecarga for delegada a outra, adicione um <remarks> elemento conciso.
  6. Em métodos que podem lançar exceções, adicione etiquetas <exception> para cada tipo de exceção intencional. Descreva a condição que o desencadeia. Não documente exceções lançadas por auxiliares de validação de argumentos, a menos que façam parte do contrato público.
  7. Para métodos que retornam um valor, adicione <returns> com uma breve descrição do que os chamadores recebem. Evite repetir o nome do método ou o tipo gerenciado.
  8. Trabalhe primeiro com a BankAccount classe base.

A sua versão deverá assemelhar-se ao seguinte código:

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() { }
}

Quando terminar, abra o arquivo XML regenerado e confirme se cada membro aparece com seus novos elementos. Uma parte cortada pode ter esta aparência:

<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>

Sugestão

Mantenha resumos em uma única frase. Se precisar de mais de um, mova o contexto secundário para <remarks>.

Use <inheritdoc/> em classes derivadas

Se tu derivares de BankAccount (por exemplo, um SavingsAccount que aplica juros), podes herdar a documentação base em vez de a copiar. Adicione um elemento de fechamento <inheritdoc/> automático dentro do bloco de documentação do membro derivado. Ainda pode acrescentar mais elementos (como detalhes extra <remarks>) depois de <inheritdoc/> para documentar o comportamento especializado:

/// <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

Observação

<inheritdoc/> reduz a duplicação e ajuda a manter a consistência quando você atualiza a documentação do tipo base mais tarde.

Depois de terminar de documentar a superfície pública, crie uma última vez para confirmar que não há avisos CS1591 restantes. Seu projeto agora produz IntelliSense útil e um arquivo XML estruturado pronto para publicar fluxos de trabalho.

Pode ver o exemplo completo anotado na pasta de origem do repositório dotnet/docs no GitHub.

Criar saída a partir de comentários

Você pode explorar mais experimentando qualquer uma destas ferramentas para criar saída a partir de comentários XML:

  • DocFX: DocFX é um gerador de documentação de API para .NET, que atualmente suporta C#, Visual Basic e F#. Ele também permite que você personalize a documentação de referência gerada. DocFX constrói um site HTML estático a partir do seu código fonte e arquivos Markdown. Além disso, DocFX fornece a flexibilidade para personalizar o layout e estilo do seu site através de modelos. Você também pode criar modelos personalizados.
  • Sandcastle: As ferramentas do Sandcastle criam arquivos de ajuda para bibliotecas de classes gerenciadas contendo páginas de referência conceituais e de API. As ferramentas do Sandcastle são baseadas em linha de comando e não têm front-end GUI, recursos de gerenciamento de projetos ou processo de compilação automatizado. O Sandcastle Help File Builder fornece GUI independente e ferramentas baseadas em linha de comando para criar um arquivo de ajuda de forma automatizada. Um pacote de integração do Visual Studio também está disponível para que os projetos de ajuda possam ser criados e gerenciados inteiramente de dentro do Visual Studio.
  • Doxygen: O Doxygen gera um navegador de documentação on-line (em HTML) ou um manual de referência offline (em LaTeX) a partir de um conjunto de arquivos de origem documentados. Há também suporte para geração de saída em RTF (MS Word), PostScript, PDF com hiperlinks, HTML compactado, DocBook e páginas de manual Unix. Você pode configurar o Doxygen para extrair a estrutura de código de arquivos de origem não documentados.