From c9b6b2b6e3d507ad3c7b6b11ef2f4af42b5928b1 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 10 Oct 2025 16:36:40 -0400 Subject: [PATCH 1/6] Update samples as needed, part 1 --- docs/csharp/fundamentals/tutorials/classes.md | 34 +- docs/csharp/fundamentals/tutorials/oop.md | 40 +-- .../introduction-to-classes/transaction.cs | 14 +- .../transaction.cs | 14 +- .../snippets/xml-documentation/BankAccount.cs | 192 +++++++++++ .../xml-documentation/GiftCardAccount.cs | 48 +++ .../InterestEarningAccount.cs | 44 +++ .../xml-documentation/LineOfCreditAccount.cs | 57 ++++ .../snippets/xml-documentation/Program.cs | 65 ++++ .../object-oriented-programming.csproj | 13 + .../snippets/xml-documentation/transaction.cs | 9 + .../tutorials/xml-documentation.md | 309 ++++++++++++++++++ 12 files changed, 776 insertions(+), 63 deletions(-) create mode 100644 docs/csharp/fundamentals/tutorials/snippets/xml-documentation/BankAccount.cs create mode 100644 docs/csharp/fundamentals/tutorials/snippets/xml-documentation/GiftCardAccount.cs create mode 100644 docs/csharp/fundamentals/tutorials/snippets/xml-documentation/InterestEarningAccount.cs create mode 100644 docs/csharp/fundamentals/tutorials/snippets/xml-documentation/LineOfCreditAccount.cs create mode 100644 docs/csharp/fundamentals/tutorials/snippets/xml-documentation/Program.cs create mode 100644 docs/csharp/fundamentals/tutorials/snippets/xml-documentation/object-oriented-programming.csproj create mode 100644 docs/csharp/fundamentals/tutorials/snippets/xml-documentation/transaction.cs create mode 100644 docs/csharp/fundamentals/tutorials/xml-documentation.md diff --git a/docs/csharp/fundamentals/tutorials/classes.md b/docs/csharp/fundamentals/tutorials/classes.md index 5ae6454e5bdef..7d56e1df8794b 100644 --- a/docs/csharp/fundamentals/tutorials/classes.md +++ b/docs/csharp/fundamentals/tutorials/classes.md @@ -1,11 +1,11 @@ --- title: Classes and objects tutorial description: Create your first C# program and explore object oriented concepts -ms.date: 02/25/2022 +ms.date: 10/10/2025 --- # Explore object oriented programming with classes and objects -In this tutorial, you'll build a console application and see the basic object-oriented features that are part of the C# language. +In this tutorial, you build a console application and see the basic object-oriented features that are part of the C# language. ## Prerequisites @@ -13,7 +13,7 @@ In this tutorial, you'll build a console application and see the basic object-or ## Create your application -Using a terminal window, create a directory named *Classes*. You'll build your application there. Change to that directory and type `dotnet new console` in the console window. This command creates your application. Open *Program.cs*. It should look like this: +Using a terminal window, create a directory named *Classes*. You build your application there. Change to that directory and type `dotnet new console` in the console window. This command creates your application. Open *Program.cs*. It should look like this: ```csharp // See https://aka.ms/new-console-template for more information @@ -22,7 +22,7 @@ Console.WriteLine("Hello, World!"); In this tutorial, you're going to create new types that represent a bank account. Typically developers define each class in a different text file. That makes it easier to manage as a program grows in size. Create a new file named *BankAccount.cs* in the *Classes* directory. -This file will contain the definition of a ***bank account***. Object Oriented programming organizes code by creating types in the form of ***classes***. These classes contain the code that represents a specific entity. The `BankAccount` class represents a bank account. The code implements specific operations through methods and properties. In this tutorial, the bank account supports this behavior: +This file contains the definition of a ***bank account***. Object Oriented programming organizes code by creating types in the form of ***classes***. These classes contain the code that represents a specific entity. The `BankAccount` class represents a bank account. The code implements specific operations through methods and properties. In this tutorial, the bank account supports this behavior: 1. It has a 10-digit number that uniquely identifies the bank account. 1. It has a string that stores the name or names of the owners. @@ -55,7 +55,7 @@ public class BankAccount } ``` -Before going on, let's take a look at what you've built. The `namespace` declaration provides a way to logically organize your code. This tutorial is relatively small, so you'll put all the code in one namespace. +Before going on, let's take a look at what you built. The `namespace` declaration provides a way to logically organize your code. This tutorial is relatively small, so you put all the code in one namespace. `public class BankAccount` defines the class, or type, you're creating. Everything inside the `{` and `}` that follows the class declaration defines the state and behavior of the class. There are five ***members*** of the `BankAccount` class. The first three are ***properties***. Properties are data elements and can have code that enforces validation or other rules. The last two are ***methods***. Methods are blocks of code that perform a single function. Reading the names of each of the members should provide enough information for you or another developer to understand what the class does. @@ -63,7 +63,7 @@ Before going on, let's take a look at what you've built. The `namespace` declar The first feature to implement is to open a bank account. When a customer opens an account, they must supply an initial balance, and information about the owner or owners of that account. -Creating a new object of the `BankAccount` type means defining a ***constructor*** that assigns those values. A ***constructor*** is a member that has the same name as the class. It's used to initialize objects of that class type. Add the following constructor to the `BankAccount` type. Place the following code above the declaration of `MakeDeposit`: +Creating a new object of the `BankAccount` type requires defining a ***constructor*** that assigns those values. A ***constructor*** is a member that has the same name as the class. It's used to initialize objects of that class type. Add the following constructor to the `BankAccount` type. Place the following code above the declaration of `MakeDeposit`: ```csharp public BankAccount(string name, decimal initialBalance) @@ -73,7 +73,7 @@ public BankAccount(string name, decimal initialBalance) } ``` -The preceding code identifies the properties of the object being constructed by including the `this` qualifier. That qualifier is usually optional and omitted. You could also have written: +The preceding code identifies the properties of the object being constructed by including the `this` qualifier. That qualifier is usually optional and omitted. You could also write: ```csharp public BankAccount(string name, decimal initialBalance) @@ -94,9 +94,9 @@ var account = new BankAccount("", 1000); Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} initial balance."); ``` -Let's run what you've built so far. If you're using Visual Studio, Select **Start without debugging** from the **Debug** menu. If you're using a command line, type `dotnet run` in the directory where you've created your project. +Let's run what you built so far. If you're using Visual Studio, Select **Start without debugging** from the **Debug** menu. If you're using a command line, type `dotnet run` in the directory where you created your project. -Did you notice that the account number is blank? It's time to fix that. The account number should be assigned when the object is constructed. But it shouldn't be the responsibility of the caller to create it. The `BankAccount` class code should know how to assign new account numbers. A simple way is to start with a 10-digit number. Increment it when each new account is created. Finally, store the current account number when an object is constructed. +Did you notice that the account number is blank? It's time to fix that. The account number should be assigned when the object is constructed. But it shouldn't be the responsibility of the caller to create it. The `BankAccount` class code should know how to assign new account numbers. A simple way is to start with a 10-digit number. Increment it when each new account is created. Finally, store the current account number when an object is constructed. Add a member declaration to the `BankAccount` class. Place the following line of code after the opening brace `{` at the beginning of the `BankAccount` class: @@ -104,7 +104,7 @@ Add a member declaration to the `BankAccount` class. Place the following line of private static int s_accountNumberSeed = 1234567890; ``` -The `accountNumberSeed` is a data member. It's `private`, which means it can only be accessed by code inside the `BankAccount` class. It's a way of separating the public responsibilities (like having an account number) from the private implementation (how account numbers are generated). It's also `static`, which means it's shared by all of the `BankAccount` objects. The value of a non-static variable is unique to each instance of the `BankAccount` object. The `accountNumberSeed` is a `private static` field and thus has the `s_` prefix as per C# naming conventions. The `s` denoting `static` and `_` denoting `private` field. Add the following two lines to the constructor to assign the account number. Place them after the line that says `this.Balance = initialBalance`: +The `accountNumberSeed` is a data member. It's `private`, which means it can only be accessed by code inside the `BankAccount` class. It's a way of separating the public responsibilities (like having an account number) from the private implementation (how account numbers are generated). It's also `static`, which means all `BankAccount` objects share the same single instance of this variable. The value of a non-static variable is unique to each instance of the `BankAccount` object. The `accountNumberSeed` is a `private static` field and thus has the `s_` prefix as per C# naming conventions. The `s` denoting `static` and `_` denoting `private` field. To initialize each account number, add the following two lines to the constructor. Place them after the line that says `this.Balance = initialBalance`: ```csharp Number = s_accountNumberSeed.ToString(); @@ -117,7 +117,7 @@ Type `dotnet run` to see the results. Your bank account class needs to accept deposits and withdrawals to work correctly. Let's implement deposits and withdrawals by creating a journal of every transaction for the account. Tracking every transaction has a few advantages over simply updating the balance on each transaction. The history can be used to audit all transactions and manage daily balances. Computing the balance from the history of all transactions when needed ensures any errors in a single transaction that are fixed will be correctly reflected in the balance on the next computation. -Let's start by creating a new type to represent a transaction. The transaction is a simple type that doesn't have any responsibilities. It needs a few properties. Create a new file named *Transaction.cs*. Add the following code to it: +Start by creating a new type that represents a transaction. The transaction is a `record` type that doesn't have any responsibilities. It needs a few properties. Create a new file named *Transaction.cs*. Add the following code to it: :::code language="csharp" source="./snippets/introduction-to-classes/Transaction.cs"::: @@ -125,19 +125,19 @@ Now, let's add a of `Transaction` obj :::code language="csharp" source="./snippets/introduction-to-classes/BankAccount.cs" id="TransactionDeclaration"::: -Now, let's correctly compute the `Balance`. The current balance can be found by summing the values of all transactions. As the code is currently, you can only get the initial balance of the account, so you'll have to update the `Balance` property. Replace the line `public decimal Balance { get; }` in *BankAccount.cs* with the following code: +Now, let's correctly compute the `Balance`. The current balance can be found by summing the values of all transactions. As the code is currently, you can only get the initial balance of the account, so you have to update the `Balance` property. Replace the line `public decimal Balance { get; }` in *BankAccount.cs* with the following code: :::code language="csharp" source="./snippets/introduction-to-classes/BankAccount.cs" id="BalanceComputation"::: This example shows an important aspect of ***properties***. You're now computing the balance when another programmer asks for the value. Your computation enumerates all transactions, and provides the sum as the current balance. -Next, implement the `MakeDeposit` and `MakeWithdrawal` methods. These methods will enforce the final two rules: the initial balance must be positive, and any withdrawal must not create a negative balance. +Next, implement the `MakeDeposit` and `MakeWithdrawal` methods. These methods enforce the final two rules: the initial balance must be positive, and any withdrawal must not create a negative balance. These rules introduce the concept of ***exceptions***. The standard way of indicating that a method can't complete its work successfully is to throw an exception. The type of exception and the message associated with it describe the error. Here, the `MakeDeposit` method throws an exception if the amount of the deposit isn't greater than 0. The `MakeWithdrawal` method throws an exception if the withdrawal amount isn't greater than 0, or if applying the withdrawal results in a negative balance. Add the following code after the declaration of the `_allTransactions` list: :::code language="csharp" source="./snippets/introduction-to-classes/BankAccount.cs" id="DepositAndWithdrawal"::: -The [`throw` statement](../../language-reference/statements/exception-handling-statements.md#the-throw-statement) **throws** an exception. Execution of the current block ends, and control transfers to the first matching `catch` block found in the call stack. You'll add a `catch` block to test this code a little later on. +The [`throw` statement](../../language-reference/statements/exception-handling-statements.md#the-throw-statement) **throws** an exception. Execution of the current block ends, and control transfers to the first matching `catch` block found in the call stack. You add a `catch` block to test this code a little later on. The constructor should get one change so that it adds an initial transaction, rather than updating the balance directly. Since you already wrote the `MakeDeposit` method, call it from your constructor. The finished constructor should look like this: @@ -169,7 +169,7 @@ catch (ArgumentOutOfRangeException e) } ``` -You use the [`try-catch` statement](../../language-reference/statements/exception-handling-statements.md#the-try-catch-statement) to mark a block of code that may throw exceptions and to catch those errors that you expect. You can use the same technique to test the code that throws an exception for a negative balance. Add the following code before the declaration of `invalidAccount` in your `Main` method: +You use the [`try-catch` statement](../../language-reference/statements/exception-handling-statements.md#the-try-catch-statement) to mark a block of code that might throw exceptions and to catch those errors that you expect. You can use the same technique to test the code that throws an exception for a negative balance. Add the following code before the declaration of `invalidAccount` in your `Main` method: ```csharp // Test for a negative balance. @@ -192,7 +192,7 @@ To finish this tutorial, you can write the `GetAccountHistory` method that creat :::code language="csharp" source="./snippets/introduction-to-classes/BankAccount.cs" id="History"::: -The history uses the class to format a string that contains one line for each transaction. You've seen the string formatting code earlier in these tutorials. One new character is `\t`. That inserts a tab to format the output. +The history uses the class to format a string that contains one line for each transaction. You used string formatting earlier in these tutorials. One new character is `\t`. That inserts a tab to format the output. Add this line to test it in *Program.cs*: @@ -200,7 +200,7 @@ Add this line to test it in *Program.cs*: Console.WriteLine(account.GetAccountHistory()); ``` -Run your program to see the results. +Run your program and check the results. ## Next steps diff --git a/docs/csharp/fundamentals/tutorials/oop.md b/docs/csharp/fundamentals/tutorials/oop.md index ac8f3a331a3d7..e4fc84c0758cf 100644 --- a/docs/csharp/fundamentals/tutorials/oop.md +++ b/docs/csharp/fundamentals/tutorials/oop.md @@ -1,7 +1,7 @@ --- title: "Object-Oriented Programming" description: C# provides full support for object-oriented programming including abstraction, encapsulation, inheritance, and polymorphism. -ms.date: 02/25/2022 +ms.date: 10/10/2025 --- # Object-Oriented programming (C#) @@ -14,15 +14,15 @@ C# is an object-oriented programming language. The four basic principles of obje In the preceding tutorial, [introduction to classes](classes.md) you saw both *abstraction* and *encapsulation*. The `BankAccount` class provided an abstraction for the concept of a bank account. You could modify its implementation without affecting any of the code that used the `BankAccount` class. Both the `BankAccount` and `Transaction` classes provide encapsulation of the components needed to describe those concepts in code. -In this tutorial, you'll extend that application to make use of *inheritance* and *polymorphism* to add new features. You'll also add features to the `BankAccount` class, taking advantage of the *abstraction* and *encapsulation* techniques you learned in the preceding tutorial. +In this tutorial, you extend that application to make use of *inheritance* and *polymorphism* to add new features. You also add features to the `BankAccount` class, taking advantage of the *abstraction* and *encapsulation* techniques you learned in the preceding tutorial. ## Create different types of accounts -After building this program, you get requests to add features to it. It works great in the situation where there is only one bank account type. Over time, needs change, and related account types are requested: +After building this program, you get requests to add features to it. It works great in the situation where there's only one bank account type. Over time, needs change, and related account types are requested: - An interest earning account that accrues interest at the end of each month. - A line of credit that can have a negative balance, but when there's a balance, there's an interest charge each month. -- A pre-paid gift card account that starts with a single deposit, and only can be paid off. It can be refilled once at the start of each month. +- A prepaid gift card account that starts with a single deposit, and only can be paid off. It can be refilled once at the start of each month. All of these different accounts are similar to `BankAccount` class defined in the earlier tutorial. You could copy that code, rename the classes, and make modifications. That technique would work in the short term, but it would be more work over time. Any changes would be copied across all the affected classes. @@ -42,28 +42,28 @@ public class GiftCardAccount : BankAccount } ``` -Each of these classes *inherits* the shared behavior from their shared *base class*, the `BankAccount` class. Write the implementations for new and different functionality in each of the *derived classes*. These derived classes already have all the behavior defined in the `BankAccount` class. +Each of these classes *inherits* the shared behavior from their shared *base class*, the `BankAccount` class. Write the implementations for new and different functionality in each of the *derived classes*. These derived classes already have all the behavior defined in the `BankAccount` class. It's a good practice to create each new class in a different source file. In [Visual Studio](https://visualstudio.com), you can right-click on the project, and select *add class* to add a new class in a new file. In [Visual Studio Code](https://code.visualstudio.com), select *File* then *New* to create a new source file. In either tool, name the file to match the class: *InterestEarningAccount.cs*, *LineOfCreditAccount.cs*, and *GiftCardAccount.cs*. -When you create the classes as shown in the preceding sample, you'll find that none of your derived classes compile. A constructor is responsible for initializing an object. A derived class constructor must initialize the derived class, and provide instructions on how to initialize the base class object included in the derived class. The proper initialization normally happens without any extra code. The `BankAccount` class declares one public constructor with the following signature: +When you create the classes as shown in the preceding sample, you find that none of your derived classes compile. A constructor is responsible for initializing an object. A derived class constructor must initialize the derived class, and provide instructions on how to initialize the base class object included in the derived class. The proper initialization normally happens without any extra code. The `BankAccount` class declares one public constructor with the following signature: ```csharp public BankAccount(string name, decimal initialBalance) ``` -The compiler doesn't generate a default constructor when you define a constructor yourself. That means each derived class must explicitly call this constructor. You declare a constructor that can pass arguments to the base class constructor. The following code shows the constructor for the `InterestEarningAccount`: +The compiler doesn't generate a default constructor when you define a constructor yourself. That means each derived class must explicitly call this constructor. You declare a constructor that can pass arguments to the base class constructor. The following code shows the constructor for the `InterestEarningAccount`: :::code language="csharp" source="./snippets/object-oriented-programming/InterestEarningAccount.cs" ID="DerivedConstructor"::: -The parameters to this new constructor match the parameter type and names of the base class constructor. You use the `: base()` syntax to indicate a call to a base class constructor. Some classes define multiple constructors, and this syntax enables you to pick which base class constructor you call. Once you've updated the constructors, you can develop the code for each of the derived classes. The requirements for the new classes can be stated as follows: +The parameters to this new constructor match the parameter type and names of the base class constructor. You use the `: base()` syntax to indicate a call to a base class constructor. Some classes define multiple constructors, and this syntax enables you to pick which base class constructor you call. Once you update the constructors, you can develop the code for each of the derived classes. The requirements for the new classes can be stated as follows: - An interest earning account: - - Will get a credit of 2% of the month-ending-balance. + - Gets a credit of 2% of the month-ending-balance. - A line of credit: - Can have a negative balance, but not be greater in absolute value than the credit limit. - - Will incur an interest charge each month where the end of month balance isn't 0. - - Will incur a fee on each withdrawal that goes over the credit limit. + - Incurs an interest charge each month where the end of month balance isn't 0. + - Incurs a fee on each withdrawal that goes over the credit limit. - A gift card account: - Can be refilled with a specified amount once each month, on the last day of the month. @@ -71,7 +71,7 @@ You can see that all three of these account types have an action that takes plac :::code language="csharp" source="./snippets/object-oriented-programming/BankAccount.cs" ID="DeclareMonthEndTransactions"::: -The preceding code shows how you use the `virtual` keyword to declare a method in the base class that a derived class may provide a different implementation for. A `virtual` method is a method where any derived class may choose to reimplement. The derived classes use the `override` keyword to define the new implementation. Typically you refer to this as "overriding the base class implementation". The `virtual` keyword specifies that derived classes may override the behavior. You can also declare `abstract` methods where derived classes must override the behavior. The base class does not provide an implementation for an `abstract` method. Next, you need to define the implementation for two of the new classes you've created. Start with the `InterestEarningAccount`: +The preceding code shows how you use the `virtual` keyword to declare a method in the base class that a derived class can provide a different implementation for. A `virtual` method is a method where any derived class can choose to reimplement. When a derived class defines a new implementation, it's called *overriding the base class implementation*. The derived classes use the `override` keyword to define the new implementation. The `virtual` keyword specifies that derived classes can override the behavior. You can also declare `abstract` methods where derived classes must override the behavior. The base class doesn't provide an implementation for an `abstract` method. Next, you need to define the implementation for two of the new classes you created. Start with the `InterestEarningAccount`: :::code language="csharp" source="./snippets/object-oriented-programming/InterestEarningAccount.cs" ID="ApplyMonthendInterest"::: @@ -87,7 +87,7 @@ The constructor provides a default value for the `monthlyDeposit` value so calle :::code language="csharp" source="./snippets/object-oriented-programming/GiftCardAccount.cs" ID="AddMonthlyDeposit"::: -The override applies the monthly deposit set in the constructor. Add the following code to the `Main` method to test these changes for the `GiftCardAccount` and the `InterestEarningAccount`: +The override applies the monthly deposit set in the constructor. Add the following code to the `Main` method. This code tests these changes for the `GiftCardAccount` and the `InterestEarningAccount`: :::code language="csharp" source="./snippets/object-oriented-programming/Program.cs" ID="FirstTests"::: @@ -104,7 +104,7 @@ lineOfCredit.PerformMonthEndTransactions(); Console.WriteLine(lineOfCredit.GetAccountHistory()); ``` -When you add the preceding code and run the program, you'll see something like the following error: +Add the preceding code and run the program. The output should be something like the following error: ```console Unhandled exception. System.ArgumentOutOfRangeException: Amount of deposit must be positive (Parameter 'amount') @@ -115,15 +115,15 @@ Unhandled exception. System.ArgumentOutOfRangeException: Amount of deposit must ``` > [!NOTE] -> The actual output includes the full path to the folder with the project. The folder names were omitted for brevity. Also, depending on your code format, the line numbers may be slightly different. +> The actual output includes the full path to the folder with the project. The folder names were omitted for brevity. Also, depending on your code format, the line numbers might be slightly different. -This code fails because the `BankAccount` assumes that the initial balance must be greater than 0. Another assumption baked into the `BankAccount` class is that the balance can't go negative. Instead, any withdrawal that overdraws the account is rejected. Both of those assumptions need to change. The line of credit account starts at 0, and generally will have a negative balance. Also, if a customer borrows too much money, they incur a fee. The transaction is accepted, it just costs more. The first rule can be implemented by adding an optional argument to the `BankAccount` constructor that specifies the minimum balance. The default is `0`. The second rule requires a mechanism that enables derived classes to modify the default algorithm. In a sense, the base class "asks" the derived type what should happen when there's an overdraft. The default behavior is to reject the transaction by throwing an exception. +This code fails because the `BankAccount` assumes that the initial balance must be greater than 0. Another assumption baked into the `BankAccount` class is that the balance can't go negative. Instead, any withdrawal that overdraws the account is rejected. Both of those assumptions need to change. The line of credit account starts at 0, and generally has a negative balance. Also, if a customer borrows too much money, they incur a fee. The transaction is accepted, it just costs more. The first rule can be implemented by adding an optional argument to the `BankAccount` constructor that specifies the minimum balance. The default is `0`. The second rule requires a mechanism that enables derived classes to modify the default algorithm. In a sense, the base class "asks" the derived type what should happen when there's an overdraft. The default behavior is to reject the transaction by throwing an exception. -Let's start by adding a second constructor that includes an optional `minimumBalance` parameter. This new constructor does all the actions done by the existing constructor. Also, it sets the minimum balance property. You could copy the body of the existing constructor, but that means two locations to change in the future. Instead, you can use *constructor chaining* to have one constructor call another. The following code shows the two constructors and the new additional field: +Let's start by adding a second constructor that includes an optional `minimumBalance` parameter. This new constructor does all the actions done by the existing constructor. Also, it sets the minimum balance property. You could copy the body of the existing constructor, but that means two locations to change in the future. Instead, you can use *constructor chaining* to have one constructor call another. The following code shows the two constructors and the new field: :::code language="csharp" source="./snippets/object-oriented-programming/BankAccount.cs" ID="ConstructorModifications"::: -The preceding code shows two new techniques. First, the `minimumBalance` field is marked as `readonly`. That means the value cannot be changed after the object is constructed. Once a `BankAccount` is created, the `minimumBalance` can't change. Second, the constructor that takes two parameters uses `: this(name, initialBalance, 0) { }` as its implementation. The `: this()` expression calls the other constructor, the one with three parameters. This technique allows you to have a single implementation for initializing an object even though client code can choose one of many constructors. +The preceding code shows two new techniques. First, the `minimumBalance` field is marked as `readonly`. That means the value can't be changed after the object is constructed. Once a `BankAccount` is created, the `minimumBalance` can't change. Second, the constructor that takes two parameters uses `: this(name, initialBalance, 0) { }` as its implementation. The `: this()` expression calls the other constructor, the one with three parameters. This technique allows you to have a single implementation for initializing an object even though client code can choose one of many constructors. This implementation calls `MakeDeposit` only if the initial balance is greater than `0`. That preserves the rule that deposits must be positive, yet lets the credit account open with a `0` balance. @@ -165,7 +165,7 @@ Replace it with the following code: :::code language="csharp" source="./snippets/object-oriented-programming/BankAccount.cs" ID="RefactoredMakeWithdrawal"::: -The added method is `protected`, which means that it can be called only from derived classes. That declaration prevents other clients from calling the method. It's also `virtual` so that derived classes can change the behavior. The return type is a `Transaction?`. The `?` annotation indicates that the method may return `null`. Add the following implementation in the `LineOfCreditAccount` to charge a fee when the withdrawal limit is exceeded: +The added method is `protected`, which means that it can be called only from derived classes. That declaration prevents other clients from calling the method. It's also `virtual` so that derived classes can change the behavior. The return type is a `Transaction?`. The `?` annotation indicates that the method can return `null`. Add the following implementation in the `LineOfCreditAccount`. This code charges a fee when the withdrawal limit is exceeded: :::code language="csharp" source="./snippets/object-oriented-programming/LineOfCreditAccount.cs" ID="AddOverdraftFee"::: @@ -183,5 +183,5 @@ This tutorial demonstrated many of the techniques used in Object-Oriented progra - You used *Abstraction* when you defined classes for each of the different account types. Those classes described the behavior for that type of account. - You used *Encapsulation* when you kept many details `private` in each class. -- You used *Inheritance* when you leveraged the implementation already created in the `BankAccount` class to save code. +- You used *Inheritance* when you used the implementation already created in the `BankAccount` class to save code. - You used *Polymorphism* when you created `virtual` methods that derived classes could override to create specific behavior for that account type. diff --git a/docs/csharp/fundamentals/tutorials/snippets/introduction-to-classes/transaction.cs b/docs/csharp/fundamentals/tutorials/snippets/introduction-to-classes/transaction.cs index 4e37cb5913d64..e4a5265d60091 100644 --- a/docs/csharp/fundamentals/tutorials/snippets/introduction-to-classes/transaction.cs +++ b/docs/csharp/fundamentals/tutorials/snippets/introduction-to-classes/transaction.cs @@ -1,15 +1,3 @@ namespace Classes; -public class Transaction -{ - public decimal Amount { get; } - public DateTime Date { get; } - public string Notes { get; } - - public Transaction(decimal amount, DateTime date, string note) - { - Amount = amount; - Date = date; - Notes = note; - } -} +public record Transaction(decimal Amount, DateTime Date, string Notes); diff --git a/docs/csharp/fundamentals/tutorials/snippets/object-oriented-programming/transaction.cs b/docs/csharp/fundamentals/tutorials/snippets/object-oriented-programming/transaction.cs index 0ae2c35ec037d..9b5bccec4f394 100644 --- a/docs/csharp/fundamentals/tutorials/snippets/object-oriented-programming/transaction.cs +++ b/docs/csharp/fundamentals/tutorials/snippets/object-oriented-programming/transaction.cs @@ -1,15 +1,3 @@ namespace OOProgramming; -public class Transaction -{ - public decimal Amount { get; } - public DateTime Date { get; } - public string Notes { get; } - - public Transaction(decimal amount, DateTime date, string note) - { - Amount = amount; - Date = date; - Notes = note; - } -} +public record Transaction(decimal Amount, DateTime Date, string Notes); diff --git a/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/BankAccount.cs b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/BankAccount.cs new file mode 100644 index 0000000000000..f8c16bec9c9c5 --- /dev/null +++ b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/BankAccount.cs @@ -0,0 +1,192 @@ +namespace OOProgramming; + +/// +/// 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. +/// +public class BankAccount +{ + /// + /// Gets the unique account number for this bank account. + /// + /// A string representation of the account number, generated sequentially. + public string Number { get; } + + /// + /// Gets or sets the name of the account owner. + /// + /// The full name of the person who owns this account. + public string Owner { get; set; } + + /// + /// Gets the current balance of the account by calculating the sum of all transactions. + /// + /// The current account balance as a decimal value. + public decimal Balance + { + get + { + decimal balance = 0; + foreach (var item in _allTransactions) + { + balance += item.Amount; + } + + return balance; + } + } + + private static int s_accountNumberSeed = 1234567890; + + private readonly decimal _minimumBalance; + + /// + /// Initializes a new instance of the BankAccount class with the specified owner name and initial balance. + /// Uses a default minimum balance of 0. + /// + /// The name of the account owner. + /// The initial deposit amount for the account. + /// + /// 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. + /// + public BankAccount(string name, decimal initialBalance) : this(name, initialBalance, 0) { } + + /// + /// Initializes a new instance of the BankAccount class with the specified owner name, initial balance, and minimum balance constraint. + /// + /// The name of the account owner. + /// The initial deposit amount for the account. + /// The minimum balance that must be maintained in the account. + /// + /// 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 + /// method. + /// + 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 _allTransactions = new(); + + /// + /// Makes a deposit to the account by adding a positive transaction. + /// + /// The amount to deposit. Must be positive. + /// The date when the deposit is made. + /// A descriptive note about the deposit transaction. + /// Thrown when the deposit amount is zero or negative. + /// + /// This method creates a new 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. + /// + 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); + } + + /// + /// Makes a withdrawal from the account by adding a negative transaction. + /// Checks withdrawal limits and minimum balance constraints before processing. + /// + /// The amount to withdraw. Must be positive. + /// The date when the withdrawal is made. + /// A descriptive note about the withdrawal transaction. + /// Thrown when the withdrawal amount is zero or negative. + /// Thrown when the withdrawal would cause the balance to fall below the minimum balance. + /// + /// This method first validates that the withdrawal amount is positive, then checks if the withdrawal would + /// violate the minimum balance constraint by calling . 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. + /// + 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); + } + + /// + /// 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. + /// + /// True if the withdrawal would cause the balance to fall below the minimum balance. + /// A Transaction object representing any overdraft fees or penalties, or null if no additional charges apply. + /// Thrown when the withdrawal would cause an overdraft and the account type doesn't allow it. + protected virtual Transaction? CheckWithdrawalLimit(bool isOverdrawn) + { + if (isOverdrawn) + { + throw new InvalidOperationException("Not sufficient funds for this withdrawal"); + } + else + { + return default; + } + } + + /// + /// Generates a detailed account history report showing all transactions with running balance calculations. + /// + /// A formatted string containing the complete transaction history with dates, amounts, running balances, and notes. + /// + /// 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. + /// + 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(); + } + + // Added for OO tutorial: + + /// + /// 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. + /// + /// + /// 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. + /// + public virtual void PerformMonthEndTransactions() { } +} diff --git a/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/GiftCardAccount.cs b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/GiftCardAccount.cs new file mode 100644 index 0000000000000..9ec4f25976d95 --- /dev/null +++ b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/GiftCardAccount.cs @@ -0,0 +1,48 @@ +namespace OOProgramming; + +/// +/// Represents a gift card account that extends with optional recurring monthly deposits. +/// Designed for prepaid gift cards that can optionally receive automatic monthly top-ups. +/// +/// +/// A gift card account is a specialized prepaid account that can be loaded with an initial balance and optionally +/// configured to receive automatic monthly deposits. This account type is ideal for gift cards, allowances, or +/// subscription-based funding scenarios. The account uses the standard minimum balance of $0 from the base +/// class, preventing overdrafts while allowing the balance to reach zero. +/// Monthly deposits, when configured, provide a convenient way to automatically replenish the account balance. +/// +public class GiftCardAccount : BankAccount +{ + private readonly decimal _monthlyDeposit = 0m; + + /// + /// Initializes a new instance of the class with the specified owner name, initial balance, and optional monthly deposit amount. + /// + /// The name of the account owner or gift card recipient. + /// The initial amount loaded onto the gift card. + /// The optional amount to be automatically deposited each month. Defaults to 0 (no monthly deposits). + /// + /// This constructor creates a gift card account with an initial balance and an optional recurring monthly deposit. + /// The monthly deposit parameter allows for automatic top-ups, making this suitable for allowance accounts or + /// subscription-based gift cards. If monthlyDeposit is 0 or not specified, no automatic deposits will occur. + /// The account inherits standard banking functionality from with a minimum balance of $0. + /// + public GiftCardAccount(string name, decimal initialBalance, decimal monthlyDeposit = 0) : base(name, initialBalance) + => _monthlyDeposit = monthlyDeposit; + + /// + /// + /// For gift card accounts, month-end processing includes applying automatic monthly deposits when configured. + /// If a monthly deposit amount was specified during account creation and is greater than zero, the amount is + /// automatically deposited with the note "Add monthly deposit". This feature enables recurring funding scenarios + /// such as monthly allowances or subscription top-ups. If no monthly deposit was configured (_monthlyDeposit is 0), + /// no transactions are added during month-end processing. + /// + public override void PerformMonthEndTransactions() + { + if (_monthlyDeposit != 0) + { + MakeDeposit(_monthlyDeposit, DateTime.Now, "Add monthly deposit"); + } + } +} diff --git a/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/InterestEarningAccount.cs b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/InterestEarningAccount.cs new file mode 100644 index 0000000000000..15a9f77c52e29 --- /dev/null +++ b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/InterestEarningAccount.cs @@ -0,0 +1,44 @@ +namespace OOProgramming; + +/// +/// Represents an interest-earning bank account that extends with monthly interest payments. +/// Earns interest on balances above $500 at a rate of 2% annually, applied monthly. +/// +/// +/// 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 class. +/// +public class InterestEarningAccount : BankAccount +{ + /// + /// Initializes a new instance of the class with the specified owner name and initial balance. + /// + /// The name of the account owner. + /// The initial deposit amount for the account. + /// + /// This constructor calls the base constructor with a default minimum balance of $0. + /// Interest earnings will begin in the first month-end processing cycle if the balance exceeds $500. + /// The account uses the same transaction tracking and balance calculation mechanisms as the base account type. + /// + public InterestEarningAccount(string name, decimal initialBalance) : base(name, initialBalance) + { + } + + /// + /// + /// For interest-earning accounts, month-end processing includes calculating and applying interest payments on qualifying balances. + /// Interest is only earned when the account balance exceeds $500. The interest calculation uses an annual rate of 2% (0.02) + /// applied to the full balance. The interest payment is deposited as a regular transaction with the note "apply monthly interest". + /// If the balance is $500 or below, no interest is earned and no transactions are added during month-end processing. + /// + public override void PerformMonthEndTransactions() + { + if (Balance > 500m) + { + decimal interest = Balance * 0.02m; + MakeDeposit(interest, DateTime.Now, "apply monthly interest"); + } + } +} diff --git a/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/LineOfCreditAccount.cs b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/LineOfCreditAccount.cs new file mode 100644 index 0000000000000..3e6dac527be6e --- /dev/null +++ b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/LineOfCreditAccount.cs @@ -0,0 +1,57 @@ +namespace OOProgramming; + +/// +/// Represents a line of credit account that extends with credit limit functionality. +/// Allows negative balances up to a specified credit limit and applies monthly interest charges on outstanding balances. +/// +/// +/// A line of credit account differs from a regular bank account in that it allows the balance to go negative +/// up to a predefined credit limit. When the balance is negative (indicating borrowed money), the account +/// accrues monthly interest charges at a rate of 7% annually. Overdraft fees of $20 are applied when +/// withdrawals exceed the available credit limit. +/// +class LineOfCreditAccount : BankAccount +{ + /// + /// Initializes a new instance of the class with the specified owner name, initial balance, and credit limit. + /// + /// The name of the account owner. + /// The initial deposit amount for the account. + /// The maximum credit limit available for borrowing. This value should be positive and represents the maximum negative balance allowed. + /// + /// The constructor converts the credit limit to a negative minimum balance by passing -creditLimit to the base constructor. + /// This allows the account to have a negative balance up to the specified credit limit. For example, a credit limit of $1000 + /// means the account can have a balance as low as -$1000. + /// + public LineOfCreditAccount(string name, decimal initialBalance, decimal creditLimit) : base(name, initialBalance, -creditLimit) + { + } + + /// + /// + /// For line of credit accounts, month-end processing includes calculating and applying interest charges on negative balances. + /// Interest is calculated at an annual rate of 7% (0.07) applied monthly to the outstanding balance. The interest charge + /// is applied as a withdrawal transaction with the note "Charge monthly interest". If the balance is positive or zero, + /// no interest charges are applied. + /// + public override void PerformMonthEndTransactions() + { + if (Balance < 0) + { + // Negate the balance to get a positive interest charge: + decimal interest = -Balance * 0.07m; + MakeWithdrawal(interest, DateTime.Now, "Charge monthly interest"); + } + } + + /// + /// + /// Line of credit accounts handle overdrafts by applying a fixed $20 overdraft fee when withdrawals exceed the available + /// credit limit. Unlike the base implementation which throws an exception, this method returns a fee transaction that + /// gets added to the account. This allows the withdrawal to proceed while documenting the penalty for exceeding the limit. + /// + protected override Transaction? CheckWithdrawalLimit(bool isOverdrawn) => + isOverdrawn + ? new Transaction(-20, DateTime.Now, "Apply overdraft fee") + : default; +} diff --git a/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/Program.cs b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/Program.cs new file mode 100644 index 0000000000000..6ecc812eedbcb --- /dev/null +++ b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/Program.cs @@ -0,0 +1,65 @@ +using OOProgramming; + +IntroToClasses(); + +// +var giftCard = new GiftCardAccount("gift card", 100, 50); +giftCard.MakeWithdrawal(20, DateTime.Now, "get expensive coffee"); +giftCard.MakeWithdrawal(50, DateTime.Now, "buy groceries"); +giftCard.PerformMonthEndTransactions(); +// can make additional deposits: +giftCard.MakeDeposit(27.50m, DateTime.Now, "add some additional spending money"); +Console.WriteLine(giftCard.GetAccountHistory()); + +var savings = new InterestEarningAccount("savings account", 10000); +savings.MakeDeposit(750, DateTime.Now, "save some money"); +savings.MakeDeposit(1250, DateTime.Now, "Add more savings"); +savings.MakeWithdrawal(250, DateTime.Now, "Needed to pay monthly bills"); +savings.PerformMonthEndTransactions(); +Console.WriteLine(savings.GetAccountHistory()); +// + +// +var lineOfCredit = new LineOfCreditAccount("line of credit", 0, 2000); +// How much is too much to borrow? +lineOfCredit.MakeWithdrawal(1000m, DateTime.Now, "Take out monthly advance"); +lineOfCredit.MakeDeposit(50m, DateTime.Now, "Pay back small amount"); +lineOfCredit.MakeWithdrawal(5000m, DateTime.Now, "Emergency funds for repairs"); +lineOfCredit.MakeDeposit(150m, DateTime.Now, "Partial restoration on repairs"); +lineOfCredit.PerformMonthEndTransactions(); +Console.WriteLine(lineOfCredit.GetAccountHistory()); +// +static void IntroToClasses() +{ + var account = new BankAccount("", 1000); + Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} balance."); + + account.MakeWithdrawal(500, DateTime.Now, "Rent payment"); + Console.WriteLine(account.Balance); + account.MakeDeposit(100, DateTime.Now, "friend paid me back"); + Console.WriteLine(account.Balance); + + Console.WriteLine(account.GetAccountHistory()); + + // Test that the initial balances must be positive: + try + { + var invalidAccount = new BankAccount("invalid", -55); + } + catch (ArgumentOutOfRangeException e) + { + Console.WriteLine("Exception caught creating account with negative balance"); + Console.WriteLine(e.ToString()); + } + + // Test for a negative balance + try + { + account.MakeWithdrawal(750, DateTime.Now, "Attempt to overdraw"); + } + catch (InvalidOperationException e) + { + Console.WriteLine("Exception caught trying to overdraw"); + Console.WriteLine(e.ToString()); + } +} diff --git a/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/object-oriented-programming.csproj b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/object-oriented-programming.csproj new file mode 100644 index 0000000000000..b8b9658ca3968 --- /dev/null +++ b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/object-oriented-programming.csproj @@ -0,0 +1,13 @@ + + + + Exe + net10.0 + OOProgramming + oo-programming + enable + enable + True + + + diff --git a/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/transaction.cs b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/transaction.cs new file mode 100644 index 0000000000000..38dc60ba7889c --- /dev/null +++ b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/transaction.cs @@ -0,0 +1,9 @@ +namespace OOProgramming; + +/// +/// Represents an immutable financial transaction with an amount, date, and descriptive notes. +/// +/// The transaction amount. Positive values represent credits/deposits, negative values represent debits/withdrawals. +/// The date and time when the transaction occurred. +/// Descriptive notes or memo text associated with the transaction. +public record Transaction(decimal Amount, DateTime Date, string Notes); diff --git a/docs/csharp/fundamentals/tutorials/xml-documentation.md b/docs/csharp/fundamentals/tutorials/xml-documentation.md new file mode 100644 index 0000000000000..6400e566636db --- /dev/null +++ b/docs/csharp/fundamentals/tutorials/xml-documentation.md @@ -0,0 +1,309 @@ +--- +title: Generate XML documentation from your source code +description: "Learn to add `///` comments that generate XML documentation directly from your source code. Learn which tags are available and how to add documentation blocks to types and members." +ms.topic: tutorial #Don't change. +ms.date: 10/10/2025 + +#customer intent: As a developer, I want to generate XML dcoumentation comments so that other developers can use my code successfully. + +--- + + +# Tutorial: Create XML documentation + +[Introduce and explain the purpose of the article.] + + + +In this tutorial, you: + +> [!div class="checklist"] +> * [Tell the user what they'll do in the tutorial] +> * [Each of these bullet points align with a key H2] +> * [Use these green checkmarks] + + + +If you don't have a service subscription, create a free +trial account . . . + + + +## Prerequisites + + + +Outline and steps: + +1. Enable XML documentation + ```xml + True + ``` +1. Do a build. Note warnings for publicly visible members without docs. Just for fun, open the XML file: + ```xml + + + + oo-programming + + + + + ``` +1. Click on the first number. Type `///` to generate a blank summary. Fill it in. This should be the `BankAccount`. Walk through one example of each type: class, property, amd method. Reader can to others themselves. +1. Build. Look at the XML file now. +```xml + + + + oo-programming + + + + + 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. + + + + + Gets the unique account number for this bank account. + + A string representation of the account number, generated sequentially. + + + + Gets or sets the name of the account owner. + + The full name of the person who owns this account. + + + + Gets the current balance of the account by calculating the sum of all transactions. + + The current account balance as a decimal value. + + + + Initializes a new instance of the BankAccount class with the specified owner name and initial balance. + Uses a default minimum balance of 0. + + The name of the account owner. + The initial deposit amount for the account. + + 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. + + + + + Initializes a new instance of the BankAccount class with the specified owner name, initial balance, and minimum balance constraint. + + The name of the account owner. + The initial deposit amount for the account. + The minimum balance that must be maintained in the account. + + 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 + method. + + + + + Makes a deposit to the account by adding a positive transaction. + + The amount to deposit. Must be positive. + The date when the deposit is made. + A descriptive note about the deposit transaction. + Thrown when the deposit amount is zero or negative. + + This method creates a new 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. + + + + + Makes a withdrawal from the account by adding a negative transaction. + Checks withdrawal limits and minimum balance constraints before processing. + + The amount to withdraw. Must be positive. + The date when the withdrawal is made. + A descriptive note about the withdrawal transaction. + Thrown when the withdrawal amount is zero or negative. + Thrown when the withdrawal would cause the balance to fall below the minimum balance. + + This method first validates that the withdrawal amount is positive, then checks if the withdrawal would + violate the minimum balance constraint by calling . 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. + + + + + 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. + + True if the withdrawal would cause the balance to fall below the minimum balance. + A Transaction object representing any overdraft fees or penalties, or null if no additional charges apply. + Thrown when the withdrawal would cause an overdraft and the account type doesn't allow it. + + + + Generates a detailed account history report showing all transactions with running balance calculations. + + A formatted string containing the complete transaction history with dates, amounts, running balances, and notes. + + 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. + + + + + 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. + + + 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. + + + + +``` +1. For derived classes, explain `` to include base docs + + +## [verb] * [noun] + +[Introduce a task and its role in completing the process.] + + + +1. Procedure step +1. Procedure step +1. Procedure step + + + +## Clean up resources + + + +## Next step -or- Related content + +> [!div class="nextstepaction"] +> [Next sequential article title](link.md) + +-or- + +* [Related article title](link.md) +* [Related article title](link.md) +* [Related article title](link.md) + + + + \ No newline at end of file From 6218a84207aa98c1f95b2346aa4b7c29697a2c1c Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 14 Oct 2025 15:47:29 -0400 Subject: [PATCH 2/6] Fill in rough draft --- .../tutorials/xml-documentation.md | 307 ++++-------------- 1 file changed, 58 insertions(+), 249 deletions(-) diff --git a/docs/csharp/fundamentals/tutorials/xml-documentation.md b/docs/csharp/fundamentals/tutorials/xml-documentation.md index 6400e566636db..9c995d67a3d6f 100644 --- a/docs/csharp/fundamentals/tutorials/xml-documentation.md +++ b/docs/csharp/fundamentals/tutorials/xml-documentation.md @@ -3,6 +3,7 @@ title: Generate XML documentation from your source code description: "Learn to add `///` comments that generate XML documentation directly from your source code. Learn which tags are available and how to add documentation blocks to types and members." ms.topic: tutorial #Don't change. ms.date: 10/10/2025 +ai-usage: ai-assisted #customer intent: As a developer, I want to generate XML dcoumentation comments so that other developers can use my code successfully. @@ -11,16 +12,9 @@ ms.date: 10/10/2025 # Tutorial: Create XML documentation -[Introduce and explain the purpose of the article.] - +In this tutorial, you’ll take an existing object‑oriented sample (from the preceding [tutorial](oop.md)) and enhance it with rich XML documentation comments so that your code becomes self-describing, surfaces helpful IntelliSense tooltips, and can participate in generated API reference docs. You’ll learn which elements deserve comments, how to use core tags like ``, ``, ``, ``, ``, ``, ``, ``, and ``, and how consistent, purposeful commenting improves maintainability, discoverability, and collaboration—without adding noise. By the end, you’ll have annotated the public surface of the sample, built the project to emit the XML documentation file, and seen how those comments flow directly into the developer experience and downstream documentation tooling. In this tutorial, you: @@ -29,58 +23,29 @@ In this tutorial, you: > * [Each of these bullet points align with a key H2] > * [Use these green checkmarks] - +- The [.NET SDK](https://dot.net) +- Either [Visual Studio](https://visualstudio.com) or [VS Code](https://visualstudio.com/vscode) with the [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit). + - Either [Visual Studio](https://visualstudio.com) or [VS Code](https://visualstudio.com/vscode) with the [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit). -If you don't have a service subscription, create a free -trial account . . . +## Enable XML documentation - +```xml +True +``` -## Prerequisites +If you're using Visual Studio, you can enable this using the "build" property page. + +Build the project. The compiler produces an XML file that aggregates all the `///` comments from publicly visible types and members. That file feeds IntelliSense tooltips, static analysis tools, and downstream documentation generation systems. + +Build the project now. You see warnings for any public members that are missing `` comments. Treat those warnings as a to‑do list that helps you provide complete, intentional documentation. Open the generated XML file (it sits next to your build output) to inspect the initial structure. At first, the `` section is empty because you haven't added comments yet: - - -Outline and steps: - -1. Enable XML documentation - ```xml - True - ``` -1. Do a build. Note warnings for publicly visible members without docs. Just for fun, open the XML file: - ```xml - - - - oo-programming - - - - - ``` -1. Click on the first number. Type `///` to generate a blank summary. Fill it in. This should be the `BankAccount`. Walk through one example of each type: class, property, amd method. Reader can to others themselves. -1. Build. Look at the XML file now. ```xml @@ -88,222 +53,66 @@ Outline and steps: oo-programming - - - 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. - - - - - Gets the unique account number for this bank account. - - A string representation of the account number, generated sequentially. - - - - Gets or sets the name of the account owner. - - The full name of the person who owns this account. - - - - Gets the current balance of the account by calculating the sum of all transactions. - - The current account balance as a decimal value. - - - - Initializes a new instance of the BankAccount class with the specified owner name and initial balance. - Uses a default minimum balance of 0. - - The name of the account owner. - The initial deposit amount for the account. - - 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. - - - - - Initializes a new instance of the BankAccount class with the specified owner name, initial balance, and minimum balance constraint. - - The name of the account owner. - The initial deposit amount for the account. - The minimum balance that must be maintained in the account. - - 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 - method. - - - - - Makes a deposit to the account by adding a positive transaction. - - The amount to deposit. Must be positive. - The date when the deposit is made. - A descriptive note about the deposit transaction. - Thrown when the deposit amount is zero or negative. - - This method creates a new 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. - - - - - Makes a withdrawal from the account by adding a negative transaction. - Checks withdrawal limits and minimum balance constraints before processing. - - The amount to withdraw. Must be positive. - The date when the withdrawal is made. - A descriptive note about the withdrawal transaction. - Thrown when the withdrawal amount is zero or negative. - Thrown when the withdrawal would cause the balance to fall below the minimum balance. - - This method first validates that the withdrawal amount is positive, then checks if the withdrawal would - violate the minimum balance constraint by calling . 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. - - - - - 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. - - True if the withdrawal would cause the balance to fall below the minimum balance. - A Transaction object representing any overdraft fees or penalties, or null if no additional charges apply. - Thrown when the withdrawal would cause an overdraft and the account type doesn't allow it. - - - - Generates a detailed account history report showing all transactions with running balance calculations. - - A formatted string containing the complete transaction history with dates, amounts, running balances, and notes. - - 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. - - - - - 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. - - - 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. - - ``` -1. For derived classes, explain `` to include base docs - - -## [verb] * [noun] - -[Introduce a task and its role in completing the process.] - - -1. Procedure step -1. Procedure step -1. Procedure step +With the file in place, start adding targeted XML comments and immediately verify how each one appears in the generated output. - - -## Clean up resources - - - -## Next step -or- Related content - -> [!div class="nextstepaction"] -> [Next sequential article title](link.md) - --or- - -* [Related article title](link.md) -* [Related article title](link.md) -* [Related article title](link.md) - - +You can explore more by trying any of these tools to create output from XML comments: - \ No newline at end of file +- [XML Doc language reference](../../language-reference/xmldoc/index.md) +- [Recommended XML tags](../../language-reference/xmldoc/recommended-tags.md) +- [XML Documentation examples](../../language-reference/xmldoc/examples.md) From 00267cd4ce3702406ab0910f08a31a58fab05ed2 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 14 Oct 2025 15:56:29 -0400 Subject: [PATCH 3/6] Proofread and finalize. --- .../tutorials/xml-documentation.md | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/docs/csharp/fundamentals/tutorials/xml-documentation.md b/docs/csharp/fundamentals/tutorials/xml-documentation.md index 9c995d67a3d6f..0021461894111 100644 --- a/docs/csharp/fundamentals/tutorials/xml-documentation.md +++ b/docs/csharp/fundamentals/tutorials/xml-documentation.md @@ -2,19 +2,13 @@ title: Generate XML documentation from your source code description: "Learn to add `///` comments that generate XML documentation directly from your source code. Learn which tags are available and how to add documentation blocks to types and members." ms.topic: tutorial #Don't change. -ms.date: 10/10/2025 +ms.date: 10/14/2025 ai-usage: ai-assisted - #customer intent: As a developer, I want to generate XML dcoumentation comments so that other developers can use my code successfully. - --- - - # Tutorial: Create XML documentation - - -In this tutorial, you’ll take an existing object‑oriented sample (from the preceding [tutorial](oop.md)) and enhance it with rich XML documentation comments so that your code becomes self-describing, surfaces helpful IntelliSense tooltips, and can participate in generated API reference docs. You’ll learn which elements deserve comments, how to use core tags like ``, ``, ``, ``, ``, ``, ``, ``, and ``, and how consistent, purposeful commenting improves maintainability, discoverability, and collaboration—without adding noise. By the end, you’ll have annotated the public surface of the sample, built the project to emit the XML documentation file, and seen how those comments flow directly into the developer experience and downstream documentation tooling. +In this tutorial, you take an existing object‑oriented sample (from the preceding [tutorial](oop.md)) and enhance it with XML documentation comments. XML documentation comments provide helpful IntelliSense tooltips, and can participate in generated API reference docs. You learn which elements deserve comments, how to use core tags like ``, ``, ``, ``, ``, ``, ``, ``, and ``, and how consistent, purposeful commenting improves maintainability, discoverability, and collaboration—without adding noise. By the end, you annotated the public surface of the sample, built the project to emit the XML documentation file, and seen how those comments flow directly into the developer experience and downstream documentation tooling. In this tutorial, you: @@ -27,8 +21,7 @@ In this tutorial, you: ## Prerequisites - The [.NET SDK](https://dot.net) -- Either [Visual Studio](https://visualstudio.com) or [VS Code](https://visualstudio.com/vscode) with the [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit). - - Either [Visual Studio](https://visualstudio.com) or [VS Code](https://visualstudio.com/vscode) with the [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit). +- Either [Visual Studio](https://visualstudio.com) or [Visual Studio Code](https://visualstudio.com/vscode) with the [C# Dev Kit](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csdevkit). ## Enable XML documentation @@ -44,7 +37,7 @@ If you're using Visual Studio, you can enable this using the "build" property pa Build the project. The compiler produces an XML file that aggregates all the `///` comments from publicly visible types and members. That file feeds IntelliSense tooltips, static analysis tools, and downstream documentation generation systems. -Build the project now. You see warnings for any public members that are missing `` comments. Treat those warnings as a to‑do list that helps you provide complete, intentional documentation. Open the generated XML file (it sits next to your build output) to inspect the initial structure. At first, the `` section is empty because you haven't added comments yet: +Build the project now. You see warnings for any public members that are missing `` comments. Treat those warnings as a to-do list that helps you provide complete, intentional documentation. Open the generated XML file (it's next to your build output) and inspect the initial structure. At first, the `` section is empty because you didn't add comments yet: ```xml @@ -63,12 +56,12 @@ With the file in place, start adding targeted XML comments and immediately verif You now cycle through the build warnings to add concise, useful documentation to the `BankAccount` type. Each warning pinpoints a public member that lacks a `` (or other required) element. Treat the warning list as a checklist. Avoid adding noise: focus on describing intent, invariants, and important usage constraints—skip restating obvious type names or parameter types. -1. Build the project again. In Visual Studio or VS Code, open the Error List / Problems panel and filter for documentation warnings (CS1591). At the command line, run a build and review the warnings emitted to the console. -1. Navigate to the first warning (the `BankAccount` class). On the line above the declaration, type `///`. The editor scaffolds a `` element. Replace the placeholder with a single, action‑focused sentence that explains the role of the account in the domain (for example, that it tracks transactions and enforces a minimum balance). -1. Add `` only if you need to explain non-obvious behavior (such as how minimum balance enforcement works or how account numbers are generated). Keep remarks short. -1. For each property (`Number`, `Owner`, `Balance`), type `///` and write a `` that states what the value represents—not how a trivial getter returns it. If a property calculates a value (like `Balance`), add a `` element that clarifies how it's derived. -1. For each constructor, add `` plus `` elements describing the meaning of each argument, not just restating the parameter name. If one overload delegates to another, note that in a concise `` element. -1. For methods that can throw, add `` tags for each intentional exception type. Describe the condition that triggers it. Do not document exceptions thrown by argument validation helpers unless they're part of the public contract. +1. Build the project again. In Visual Studio or Visual Studio Code, open the Error List / Problems panel and filter for documentation warnings (CS1591). At the command line, run a build and review the warnings emitted to the console. +1. Navigate to the first warning (the `BankAccount` class). On the line above the declaration, type `///`. The editor scaffolds a `` element. Replace the placeholder with a single, action‑focused sentence. The sentence explains the role of the account in the domain. For example, it tracks transactions and enforces a minimum balance. +1. Add `` only if you need to explain behavior. Examples include how minimum balance enforcement works or how account numbers are generated. Keep remarks short. +1. For each property (`Number`, `Owner`, `Balance`), type `///` and write a `` that states what the value represents—not how a trivial getter returns it. If a property calculates a value (like `Balance`), add a `` element that clarifies the calculation. +1. For each constructor, add `` plus `` elements describing the meaning of each argument, not just restating the parameter name. If one overload delegates to another, add a concise `` element. +1. For methods that can throw, add `` tags for each intentional exception type. Describe the condition that triggers it. Don't document exceptions thrown by argument validation helpers unless they're part of the public contract. 1. For methods that return a value, add `` with a short description of what callers receive. Avoid repeating the method name or managed type. 1. Work with the `BankAccount` base class first. @@ -90,7 +83,7 @@ When you're done, open the regenerated XML file and confirm that each member app ## Use in derived classes -If you derive from `BankAccount` (for example, a `SavingsAccount` that applies interest), you can inherit base documentation instead of copying it. Add a self-closing `` element inside the derived member's documentation block. You can still append additional elements (such as extra `` details) after `` to document the specialized behavior. +If you derive from `BankAccount` (for example, a `SavingsAccount` that applies interest), you can inherit base documentation instead of copying it. Add a self-closing `` element inside the derived member's documentation block. You can still append more elements (such as extra `` details) after `` to document the specialized behavior. ```csharp /// From 62270fbbe21f90d593b9f35d8199f9c26f3f764e Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 14 Oct 2025 16:37:20 -0400 Subject: [PATCH 4/6] Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- docs/csharp/fundamentals/tutorials/xml-documentation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/csharp/fundamentals/tutorials/xml-documentation.md b/docs/csharp/fundamentals/tutorials/xml-documentation.md index 0021461894111..76db8163e8ebe 100644 --- a/docs/csharp/fundamentals/tutorials/xml-documentation.md +++ b/docs/csharp/fundamentals/tutorials/xml-documentation.md @@ -4,7 +4,7 @@ description: "Learn to add `///` comments that generate XML documentation direct ms.topic: tutorial #Don't change. ms.date: 10/14/2025 ai-usage: ai-assisted -#customer intent: As a developer, I want to generate XML dcoumentation comments so that other developers can use my code successfully. +#customer intent: As a developer, I want to generate XML documentation comments so that other developers can use my code successfully. --- # Tutorial: Create XML documentation @@ -13,9 +13,9 @@ In this tutorial, you take an existing object‑oriented sample (from the preced In this tutorial, you: > [!div class="checklist"] -> * [Tell the user what they'll do in the tutorial] -> * [Each of these bullet points align with a key H2] -> * [Use these green checkmarks] +> * Enable XML documentation output in your C# project. +> * Add and structure XML documentation comments to types and members. +> * Build the project and inspect the generated XML documentation file. ## Prerequisites From 551c245254de71071d795d05901d488f7a88ef7b Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 14 Oct 2025 16:43:47 -0400 Subject: [PATCH 5/6] build warnings --- docs/csharp/fundamentals/tutorials/xml-documentation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/csharp/fundamentals/tutorials/xml-documentation.md b/docs/csharp/fundamentals/tutorials/xml-documentation.md index 76db8163e8ebe..fed376ec885c0 100644 --- a/docs/csharp/fundamentals/tutorials/xml-documentation.md +++ b/docs/csharp/fundamentals/tutorials/xml-documentation.md @@ -13,11 +13,11 @@ In this tutorial, you take an existing object‑oriented sample (from the preced In this tutorial, you: > [!div class="checklist"] +> > * Enable XML documentation output in your C# project. > * Add and structure XML documentation comments to types and members. > * Build the project and inspect the generated XML documentation file. - ## Prerequisites - The [.NET SDK](https://dot.net) @@ -81,7 +81,7 @@ When you're done, open the regenerated XML file and confirm that each member app > [!TIP] > Keep summaries to a single sentence. If you need more than one, move secondary context into ``. -## Use in derived classes +## Use `` in derived classes If you derive from `BankAccount` (for example, a `SavingsAccount` that applies interest), you can inherit base documentation instead of copying it. Add a self-closing `` element inside the derived member's documentation block. You can still append more elements (such as extra `` details) after `` to document the specialized behavior. From 7712f3b6316ab27b7a1c0acde2ce8808484b0590 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 16 Oct 2025 10:20:24 -0400 Subject: [PATCH 6/6] Apply suggestions from code review Co-authored-by: David Pine --- .../snippets/xml-documentation/BankAccount.cs | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/BankAccount.cs b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/BankAccount.cs index f8c16bec9c9c5..c41fa0e1ff61a 100644 --- a/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/BankAccount.cs +++ b/docs/csharp/fundamentals/tutorials/snippets/xml-documentation/BankAccount.cs @@ -4,7 +4,7 @@ /// 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. /// -public class BankAccount +public class BankAccount { /// /// Gets the unique account number for this bank account. @@ -22,19 +22,7 @@ public class BankAccount /// Gets the current balance of the account by calculating the sum of all transactions. /// /// The current account balance as a decimal value. - public decimal Balance - { - get - { - decimal balance = 0; - foreach (var item in _allTransactions) - { - balance += item.Amount; - } - - return balance; - } - } + public decimal Balance => _allTransactions.Sum(i => i.Amount); private static int s_accountNumberSeed = 1234567890; @@ -76,7 +64,7 @@ public BankAccount(string name, decimal initialBalance, decimal minimumBalance) MakeDeposit(initialBalance, DateTime.Now, "Initial balance"); } - private readonly List _allTransactions = new(); + private readonly List _allTransactions = []; /// /// Makes a deposit to the account by adding a positive transaction.