Freigeben über


Lernprogramm: Erstellen einer XML-Dokumentation

In diesem Lernprogramm nehmen Sie ein vorhandenes objektorientiertes Beispiel (aus dem vorherigen Lernprogramm) und verbessern es mit XML-Dokumentationskommentaren. XML-Dokumentationskommentare bieten hilfreiche IntelliSense-Tooltips und können in generierten API-Referenzdokumenten verwendet werden. Sie erfahren, welche Elemente Kommentare verdienen, wie Sie Kerntags wie <summary>, <param>, <returns>, <value>, <remarks>, <example>, <seealso>, <exception> und <inheritdoc> verwenden und wie konsistente, zielgerichtete Kommentare die Wartbarkeit, Auffindbarkeit und Zusammenarbeit verbessern, ohne unnötiges Rauschen zu erzeugen. Am Ende haben Sie die öffentliche Oberfläche des Beispiels kommentiert, das Projekt so erstellt, dass die XML-Dokumentationsdatei ausgegeben wird, und sie haben gesehen, wie diese Kommentare direkt in die Entwicklerumgebung und nachgeschaltete Dokumentationstools fließen.

In diesem Tutorial erfahren Sie:

  • Aktivieren Sie die XML-Dokumentationsausgabe in Ihrem C#-Projekt.
  • Fügen Sie XML-Dokumentationskommentare zu Typen und Elementen hinzu und strukturieren Sie sie.
  • Erstellen Sie das Projekt, und prüfen Sie die generierte XML-Dokumentationsdatei.

Voraussetzungen

Aktivieren der XML-Dokumentation

Laden Sie das Projekt, das Sie im vorherigen objektorientierten Lernprogramm erstellt haben. Wenn Sie lieber neu beginnen möchten, klonen Sie das Beispiel aus dem dotnet/docs Repository unter dem snippets/object-oriented-programming Ordner.

Aktivieren Sie als Nächstes die XML-Dokumentationsausgabe, damit der Compiler eine .xml Datei parallel zu Ihrer Assembly ausgibt. Bearbeiten Sie die Projektdatei, und fügen Sie die folgende Eigenschaft in einem Element hinzu <PropertyGroup> (oder bestätigen Sie sie):

<GenerateDocumentationFile>True</GenerateDocumentationFile>

Wenn Sie Visual Studio verwenden, können Sie dies mithilfe der Eigenschaftenseite "Build" aktivieren.

Projekt erstellen. Der Compiler erzeugt eine XML-Datei, die alle /// Kommentare aus öffentlich sichtbaren Typen und Membern aggregiert. Diese Datei versorgt IntelliSense-Tooltips, statische Analysetools und Systeme zur Dokumentationsgeneration.

Erstellen Sie das Projekt jetzt. Es werden Warnungen für alle öffentlichen Mitglieder angezeigt, bei denen <summary> Kommentare fehlen. Behandeln Sie diese Warnungen als to-do Liste, die Ihnen hilft, vollständige, beabsichtigte Dokumentation bereitzustellen. Öffnen Sie die generierte XML-Datei (neben der Buildausgabe), und prüfen Sie die ursprüngliche Struktur. Zunächst ist der <members> Abschnitt leer, da Sie noch keine Kommentare hinzugefügt haben:

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

Mit der Datei an Ort und Stelle beginnen Sie mit dem Hinzufügen gezielter XML-Kommentare und überprüfen Sie sofort, wie jeder Kommentar in der generierten Ausgabe angezeigt wird. Beginnen Sie mit dem Transaction Datensatztyp:

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

Hinzufügen von Dokumentationskommentaren

Jetzt gehen Sie die Buildwarnungen durch, um präzise und nützliche Dokumentation zum Typ BankAccount hinzuzufügen. Jede Warnung weist auf ein öffentliches Mitglied hin, dem ein <summary> (oder ein anderes erforderliches) Element fehlt. Behandeln Sie die Warnliste als Checkliste. Vermeiden Sie das Hinzufügen von Rauschen: Konzentrieren Sie sich auf die Beschreibung von Absichten, Invarianten und wichtigen Verwendungseinschränkungen, und überspringen Sie das Restieren offensichtlicher Typnamen oder Parametertypen.

  1. Erstellen Sie das Projekt erneut. Öffnen Sie in Visual Studio oder Visual Studio Code den Bereich "Fehlerliste/Probleme", und filtern Sie nach Dokumentationswarnungen (CS1591). Führen Sie an der Befehlszeile einen Build aus, und überprüfen Sie die Warnungen, die an die Konsole ausgegeben werden.
  2. Navigieren Sie zur ersten Warnung (klasse BankAccount ). Geben Sie in der Zeile oberhalb der Deklaration den Text ///ein. Der Editor erstellt ein Gerüst für ein <summary> Element. Ersetzen Sie den Platzhalter durch einen einzelnen satz mit Aktionsfokus. Im Satz wird die Rolle des Kontos in der Domäne erläutert. So verfolgt sie beispielsweise Transaktionen und erzwingt einen Mindestsaldo.
  3. Fügen Sie nur hinzu <remarks> , wenn Sie das Verhalten erklären müssen. Beispiele hierfür sind, wie die Mindestsaldoerzwingung funktioniert oder wie Kontonummern generiert werden. Halten Sie die Anmerkungen kurz.
  4. Geben Sie für jede Eigenschaft (Number, Owner, Balance) den Typ /// ein und schreiben Sie ein <summary>, das angibt, was der Wert darstellt—und nicht, wie ein trivialer Getter ihn zurückgibt. Wenn eine Eigenschaft einen Wert berechnet (z Balance. B. ), fügen Sie ein <value> Element hinzu, das die Berechnung verdeutlicht.
  5. Fügen Sie für jeden Konstruktor <summary>- und <param>-Elemente hinzu, die die Bedeutung jedes Arguments beschreiben, nicht nur den Namen des Parameters wiederholen. Wenn eine Überladung an eine andere delegiert wird, fügen Sie ein präzises <remarks> Element hinzu.
  6. Fügen Sie für Methoden, die ausgelöst werden können, Tags für jeden beabsichtigten Ausnahmetyp hinzu <exception> . Beschreiben Sie die Bedingung, die sie auslöst. Dokumentieren Sie ausnahmen nicht, die von Argumentüberprüfungshilfsern ausgelöst werden, es sei denn, sie sind Teil des öffentlichen Vertrags.
  7. Fügen Sie für Methoden, die einen Wert zurückgeben, <returns> mit einer kurzen Beschreibung dessen hinzu, was die Aufrufer erhalten. Vermeiden Sie das Wiederholen des Methodennamens oder des verwalteten Typs.
  8. Arbeiten Sie zuerst mit der BankAccount Basisklasse.

Ihre Version sollte etwa wie der folgende Code aussehen:

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

Wenn Sie fertig sind, öffnen Sie die neu generierte XML-Datei, und vergewissern Sie sich, dass jedes Element mit ihren neuen Elementen angezeigt wird. Ein gekürzter Teil sieht möglicherweise wie folgt aus:

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

Tipp

Behalten Sie Zusammenfassungen in einem einzelnen Satz bei. Wenn Sie mehr als einen benötigen, verschieben Sie den sekundären Kontext in <remarks>.

Verwendung <inheritdoc/> in abgeleiteten Klassen

Wenn Sie von BankAccount (z. B. einer SavingsAccount-Anwendung, die Zinsen berechnet) abgeleitet werden, können Sie die Basisdokumentation übernehmen, anstatt sie zu kopieren. Fügen Sie ein <inheritdoc/>-Element, das sich selbst schließt, innerhalb des Dokumentationsblocks des abgeleiteten Mitglieds hinzu. Sie können weiterhin weitere Elemente (z. B. zusätzliche <remarks> Details) nach <inheritdoc/> anhängen, um das spezielle Verhalten zu dokumentieren.

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

Hinweis

<inheritdoc/> reduziert duplizierung und trägt dazu bei, Konsistenz aufrechtzuerhalten, wenn Sie die Basistypdokumentation später aktualisieren.

Nachdem Sie die Dokumentation der öffentlichen Oberfläche abgeschlossen haben, kompilieren Sie ein letztes Mal, damit Sie bestätigen können, dass es keine verbleibenden CS1591-Warnungen gibt. Ihr Projekt erzeugt nun nützliche IntelliSense und eine strukturierte XML-Datei, die für Veröffentlichungsworkflows bereit ist.

Sie können das vollständige kommentierte Beispiel im Quellordner des Dotnet/docs-Repositorys auf GitHub sehen.

Ausgabe aus Kommentaren generieren

Weitere Informationen erhalten Sie, indem Sie versuchen, eine der folgenden Tools zum Erstellen einer Ausgabe aus XML-Kommentaren zu verwenden:

  • DocFX: DocFX ist ein API-Dokumentationsgenerator für .NET, der derzeit C#, Visual Basic und F# unterstützt. Außerdem können Sie die generierte Referenzdokumentation anpassen. DocFX erstellt eine statische HTML-Website aus Ihrem Quellcode und Markdown-Dateien. DocFX bietet Ihnen außerdem die Flexibilität, das Layout und den Stil Ihrer Website über Vorlagen anzupassen. Sie können auch benutzerdefinierte Vorlagen erstellen.
  • Sandcastle: Die Sandcastle-Tools erstellen Hilfedateien für verwaltete Klassenbibliotheken, die sowohl konzeptionelle als auch API-Referenzseiten enthalten. Die Sandcastle-Tools sind befehlszeilenbasiert und verfügen nicht über GUI-Front-End-, Projektverwaltungsfeatures oder automatisierten Buildprozess. Der Sandcastle-Hilfedatei-Generator bietet eigenständige GUI- und Befehlszeilentools, um eine Hilfedatei automatisch zu erstellen. Ein Visual Studio-Integrationspaket ist auch dafür verfügbar, sodass Hilfeprojekte vollständig in Visual Studio erstellt und verwaltet werden können.
  • Doxygen: Doxygen generiert einen Onlinedokumentationsbrowser (in HTML) oder ein Offlinereferenzhandbuch (in LaTeX) aus einer Reihe dokumentierter Quelldateien. Es gibt auch Unterstützung für die Ausgabe in RTF (MS Word), PostScript, verknüpftem PDF, komprimiertem HTML, DocBook und Unix-Manpages. Sie können Doxygen so konfigurieren, dass die Codestruktur aus nicht dokumentierten Quelldateien extrahiert wird.