From 7f08a00bd3a463ec2048f58641ef475cc4950222 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 15 Sep 2020 14:48:21 -0400 Subject: [PATCH 01/16] move OO tutorial This commit moves the source tutorial, updates the TOC and links, and updates the redirection file. --- .openpublishing.redirection.json | 5 +++++ .../programming-guide/classes-and-structs/classes.md | 2 +- docs/csharp/programming-guide/concepts/index.md | 1 - docs/csharp/toc.yml | 4 ++-- docs/csharp/tutorials/intro-to-csharp/index.md | 8 +++++++- .../intro-to-csharp}/object-oriented-programming.md | 0 6 files changed, 15 insertions(+), 5 deletions(-) rename docs/csharp/{programming-guide/concepts => tutorials/intro-to-csharp}/object-oriented-programming.md (100%) diff --git a/.openpublishing.redirection.json b/.openpublishing.redirection.json index baf3ec3613e82..d41c3b8e477ef 100644 --- a/.openpublishing.redirection.json +++ b/.openpublishing.redirection.json @@ -2164,6 +2164,11 @@ "redirect_url": "/dotnet/standard/linq/xdocument-class-overview", "redirect_document_id": true }, + { + "source_path": "docs/csharp/programming-guide/concepts/object-oriented-programming.md", + "redirect_url": "/dotnet/csharp/tutorials/intro-to-csharp/object-oriented-programming", + "redirect_document_id": true + }, { "source_path": "docs/csharp/programming-guide/concepts/linq/xelement-class-overview.md", "redirect_url": "/dotnet/standard/linq/xelement-class-overview", diff --git a/docs/csharp/programming-guide/classes-and-structs/classes.md b/docs/csharp/programming-guide/classes-and-structs/classes.md index dbbc1c661e23d..832d2ddecedde 100644 --- a/docs/csharp/programming-guide/classes-and-structs/classes.md +++ b/docs/csharp/programming-guide/classes-and-structs/classes.md @@ -96,7 +96,7 @@ The following example defines a public class that contains an [auto-implemented ## See also - [C# Programming Guide](../index.md) -- [Object-Oriented Programming](../concepts/object-oriented-programming.md) +- [Object-Oriented Programming](../../tutorials/intro-to-csharp/object-oriented-programming.md) - [Polymorphism](polymorphism.md) - [Identifier names](../inside-a-program/identifier-names.md) - [Members](members.md) diff --git a/docs/csharp/programming-guide/concepts/index.md b/docs/csharp/programming-guide/concepts/index.md index 4a6398fa33690..48bfb32c5bde9 100644 --- a/docs/csharp/programming-guide/concepts/index.md +++ b/docs/csharp/programming-guide/concepts/index.md @@ -19,7 +19,6 @@ This section explains programming concepts in the C# language. |[Expression Trees (C#)](./expression-trees/index.md)|Explains how you can use expression trees to enable dynamic modification of executable code.| |[Iterators (C#)](./iterators.md)|Describes iterators, which are used to step through collections and return elements one at a time.| |[Language-Integrated Query (LINQ) (C#)](./linq/index.md)|Discusses the powerful query capabilities in the language syntax of C#, and the model for querying relational databases, XML documents, datasets, and in-memory collections.| -|[Object-Oriented Programming (C#)](./object-oriented-programming.md)|Describes common object-oriented concepts, including encapsulation, inheritance, and polymorphism.| |[Reflection (C#)](./reflection.md)|Explains how to use reflection to dynamically create an instance of a type, bind the type to an existing object, or get the type from an existing object and invoke its methods or access its fields and properties.| |[Serialization (C#)](./serialization/index.md)|Describes key concepts in binary, XML, and SOAP serialization.| diff --git a/docs/csharp/toc.yml b/docs/csharp/toc.yml index d6e84314a3813..3fbb555e8970a 100644 --- a/docs/csharp/toc.yml +++ b/docs/csharp/toc.yml @@ -36,6 +36,8 @@ href: tutorials/intro-to-csharp/arrays-and-collections.md - name: Introduction to classes href: tutorials/intro-to-csharp/introduction-to-classes.md + - name: Object-Oriented programming + href: tutorials/intro-to-csharp/object-oriented-programming.md - name: Explore C# 6 href: tutorials/exploration/csharp-6.yml - name: Explore string interpolation - interactive @@ -510,8 +512,6 @@ href: programming-guide/concepts/linq/enabling-a-data-source-for-linq-querying1.md - name: Visual Studio IDE and Tools Support for LINQ href: programming-guide/concepts/linq/visual-studio-ide-and-tools-support-for-linq.md - - name: Object-Oriented Programming - href: programming-guide/concepts/object-oriented-programming.md - name: Reflection href: programming-guide/concepts/reflection.md - name: Serialization (C#) diff --git a/docs/csharp/tutorials/intro-to-csharp/index.md b/docs/csharp/tutorials/intro-to-csharp/index.md index 2bf59b2a0a7f1..979fe3669425f 100644 --- a/docs/csharp/tutorials/intro-to-csharp/index.md +++ b/docs/csharp/tutorials/intro-to-csharp/index.md @@ -69,7 +69,13 @@ This tutorial assumes that you've finished the lessons listed above. ## [Introduction to classes](introduction-to-classes.md) -This final tutorial is only available to run on your machine, using your own local development environment and .NET Core. +This tutorial is only available to run on your machine, using your own local development environment and .NET Core. You'll build a console application and see the basic object-oriented features that are part of the C# language. This tutorial assumes you've finished the online introductory tutorials, and you've installed [.NET Core SDK](https://dotnet.microsoft.com/download) and [Visual Studio Code](https://code.visualstudio.com/). + +## [Object oriented programming](object-oriented-programming.md) + +This tutorial teaches the concepts used in object-oriented programming. You'll learn the concepts of *abstraction*, *encapsulation*, *inheritance*, and *polymorphism* using C# examples. + +This tutorial assumes you've finished the online introductory tutorials, and you've installed [.NET Core SDK](https://dotnet.microsoft.com/download) and either [Visual Studio Code](https://code.visualstudio.com/) or [Visual Studio](https://visualstudio.com) on your development machine. diff --git a/docs/csharp/programming-guide/concepts/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md similarity index 100% rename from docs/csharp/programming-guide/concepts/object-oriented-programming.md rename to docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md From 5bd66294bffb1b746654f28d1db9d63b2457ede4 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 15 Sep 2020 14:58:47 -0400 Subject: [PATCH 02/16] fix build warnings. --- .../object-oriented-programming.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index f58df5bc3f79b..bc130493518f7 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -119,8 +119,8 @@ In most cases you declare a method within a class definition. However, C# also s For more information, see: -- [Methods](../classes-and-structs/methods.md) -- [Extension Methods](../classes-and-structs/extension-methods.md) +- [Methods](../../programming-guide/classes-and-structs/methods.md) +- [Extension Methods](../../programming-guide/classes-and-structs/extension-methods.md) #### Constructors @@ -138,7 +138,7 @@ public class SampleClass } ``` -For more information, see [Constructors](../classes-and-structs/constructors.md). +For more information, see [Constructors](../../programming-guide/classes-and-structs/constructors.md). #### Finalizers @@ -189,7 +189,7 @@ The following access modifiers are available: | [protected internal](../../language-reference/keywords/protected-internal.md) | The type or member can be accessed by any code in the same assembly, or by any derived class in another assembly. | | [private protected](../../language-reference/keywords/private-protected.md) | The type or member can be accessed by code in the same class or in a derived class within the base class assembly. | -For more information, see [Access Modifiers](../classes-and-structs/access-modifiers.md). +For more information, see [Access Modifiers](../../programming-guide/classes-and-structs/access-modifiers.md). ### Instantiating classes @@ -222,7 +222,7 @@ var sampleObject = new SampleClass For more information, see: - [new Operator](../../language-reference/operators/new-operator.md) -- [Object and Collection Initializers](../classes-and-structs/object-and-collection-initializers.md) +- [Object and Collection Initializers](../../programming-guide/classes-and-structs/object-and-collection-initializers.md) ### Static Classes and Members @@ -262,7 +262,7 @@ var sampleObject = new }; ``` -For more information, see: [Anonymous Types](../classes-and-structs/anonymous-types.md). +For more information, see: [Anonymous Types](../../programming-guide/classes-and-structs/anonymous-types.md). ## Inheritance @@ -334,7 +334,7 @@ class SampleClass : ISampleInterface } ``` -For more information, see the programming guide article on [Interfaces](../interfaces/index.md) and the language reference article on the [interface](../../language-reference/keywords/interface.md) keyword. +For more information, see the programming guide article on [Interfaces](../../programming-guide/interfaces/index.md) and the language reference article on the [interface](../../language-reference/keywords/interface.md) keyword. ## Generics @@ -359,7 +359,7 @@ sampleObject.Field = "Sample string"; For more information, see: - [Generics in .NET](../../../standard/generics/index.md) -- [Generics - C# Programming Guide](../generics/index.md) +- [Generics - C# Programming Guide](../../programming-guide/generics/index.md) ## Delegates @@ -394,7 +394,7 @@ class SampleClass } ``` -For more information, see the programming guide article on [Delegates](../delegates/index.md) and the language reference article on the [delegate](../../language-reference/builtin-types/reference-types.md) keyword. +For more information, see the programming guide article on [Delegates](../../programming-guide/delegates/index.md) and the language reference article on the [delegate](../../language-reference/builtin-types/reference-types.md) keyword. ## See also From d36ebddff29f7f1cd5acc25081511292754a70d9 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 18 Sep 2020 11:37:07 -0400 Subject: [PATCH 03/16] Define outline --- .../introduction-to-classes.md | 9 +++++- .../object-oriented-programming.md | 28 +++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md b/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md index 9406caddc739a..868b633d7fa8c 100644 --- a/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md +++ b/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md @@ -206,4 +206,11 @@ Run your program to see the results. If you got stuck, you can see the source for this tutorial [in our GitHub repo](https://github.com/dotnet/docs/tree/master/samples/snippets/csharp/classes-quickstart/). -Congratulations, you've finished all our introduction to C# tutorials. If you're eager to learn more, try more of our [tutorials](../index.md). +You can continue with the [object oriented programming](object-oriented-programming.md) tutorial. + +You can learn more about these concepts in these articles: + +- [If and else statement](../../language-reference/keywords/if-else.md) +- [While statement](../../language-reference/keywords/while.md) +- [Do statement](../../language-reference/keywords/do.md) +- [For statement](../../language-reference/keywords/for.md) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index bc130493518f7..484ccff07cf07 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -1,8 +1,7 @@ --- title: "Object-Oriented Programming (C#)" description: C# provides full support for object-oriented programming including abstraction, encapsulation, inheritance, and polymorphism. -ms.date: 05/13/2020 -ms.assetid: 89574786-65ef-4335-88bc-fbacd094f183 +ms.date: 09/16/2020 --- # Object-Oriented programming (C#) @@ -13,9 +12,21 @@ C# provides full support for object-oriented programming including abstraction, - *Inheritance* describes the ability to create new classes based on an existing class. - *Polymorphism* means that you can have multiple classes that can be used interchangeably, even though each class implements the same properties or methods in different ways. +In the preceding tutorial, [introduction to classes](introduction-to-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. + +Outline: + +1. Create hierarchy of interest earning account, line of credit, fixed loan. +1. Define virtual and abstract methods +1. Implement and run tests + +existing text follows to ensure we don't miss concepts. + ## Classes and objects -The terms *class* and *object* describe the *type* of objects, and the *instances* of classes, respectively. So, the act of creating an object is called *instantiation*. Using the blueprint analogy, a class is a blueprint, and an object is a building made from that blueprint. +The terms *class* describes a *type*. The term*object* describe the *instances* of that type. So, the act of creating an object is called *instantiation*. Using the blueprint analogy, a class is a blueprint, and an object is a building made from that blueprint. To define a class: @@ -396,6 +407,13 @@ class SampleClass For more information, see the programming guide article on [Delegates](../../programming-guide/delegates/index.md) and the language reference article on the [delegate](../../language-reference/builtin-types/reference-types.md) keyword. -## See also +## Next steps + +You can learn more about these concepts in these articles: + +- [If and else statement](../../language-reference/keywords/if-else.md) +- [While statement](../../language-reference/keywords/while.md) +- [Do statement](../../language-reference/keywords/do.md) +- [For statement](../../language-reference/keywords/for.md) -- [C# Programming Guide](../index.md) +Congratulations, you've finished all our introduction to C# tutorials. If you're eager to learn more, try more of our [tutorials](../index.md). From f88eaf347d171dd75044bca249a1c2b4915747cd Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 18 Sep 2020 11:47:32 -0400 Subject: [PATCH 04/16] move samples to new location --- .../introduction-to-classes.md | 12 +-- .../introduction-to-classes}/BankAccount.cs | 0 .../introduction-to-classes}/Program.cs | 0 .../introduction-to-classes}/README.md | 0 .../introduction-to-classes}/classes.csproj | 0 .../introduction-to-classes}/transaction.cs | 0 .../BankAccount.cs | 85 +++++++++++++++++++ .../object-oriented-programming/Program.cs | 42 +++++++++ .../object-oriented-programming/README.md | 24 ++++++ .../object-oriented-programming.csproj | 8 ++ .../transaction.cs | 18 ++++ 11 files changed, 183 insertions(+), 6 deletions(-) rename {samples/snippets/csharp/classes-quickstart => docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes}/BankAccount.cs (100%) rename {samples/snippets/csharp/classes-quickstart => docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes}/Program.cs (100%) rename {samples/snippets/csharp/classes-quickstart => docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes}/README.md (100%) rename {samples/snippets/csharp/classes-quickstart => docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes}/classes.csproj (100%) rename {samples/snippets/csharp/classes-quickstart => docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes}/transaction.cs (100%) create mode 100644 docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs create mode 100644 docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs create mode 100644 docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/README.md create mode 100644 docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj create mode 100644 docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/transaction.cs diff --git a/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md b/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md index 868b633d7fa8c..937516fc8cdf9 100644 --- a/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md +++ b/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md @@ -115,11 +115,11 @@ Your bank account class needs to accept deposits and withdrawals to work correct Let's start by creating a new type to represent a transaction. This 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: -[!code-csharp[Transaction](~/samples/snippets/csharp/classes-quickstart/Transaction.cs)] +[!code-csharp[Transaction](./snippets/introduction-to-classes/Transaction.cs)] Now, let's add a of `Transaction` objects to the `BankAccount` class. Add the following declaration after the constructor in your *BankAccount.cs* file: -[!code-csharp[TransactionDecl](~/samples/snippets/csharp/classes-quickstart/BankAccount.cs#TransactionDeclaration)] +[!code-csharp[TransactionDecl](./snippets/introduction-to-classes/BankAccount.cs#TransactionDeclaration)] The class requires you to import a different namespace. Add the following at the beginning of *BankAccount.cs*: @@ -129,7 +129,7 @@ using System.Collections.Generic; Now, let's change how the `Balance` is reported. It can be found by summing the values of all transactions. Modify the declaration of `Balance` in the `BankAccount` class to the following: -[!code-csharp[BalanceComputation](~/samples/snippets/csharp/classes-quickstart/BankAccount.cs#BalanceComputation)] +[!code-csharp[BalanceComputation](./snippets/introduction-to-classes/BankAccount.cs#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. @@ -137,13 +137,13 @@ Next, implement the `MakeDeposit` and `MakeWithdrawal` methods. These methods wi This introduces the concept of ***exceptions***. The standard way of indicating that a method cannot 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 is negative. The `MakeWithdrawal` method throws an exception if the withdrawal amount is negative, or if applying the withdrawal results in a negative balance. Add the following code after the declaration of the `allTransactions` list: -[!code-csharp[DepositAndWithdrawal](~/samples/snippets/csharp/classes-quickstart/BankAccount.cs#DepositAndWithdrawal)] +[!code-csharp[DepositAndWithdrawal](./snippets/introduction-to-classes/BankAccount.cs#DepositAndWithdrawal)] The [`throw`](../../language-reference/keywords/throw.md) 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 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: -[!code-csharp[Constructor](~/samples/snippets/csharp/classes-quickstart/BankAccount.cs#Constructor)] +[!code-csharp[Constructor](./snippets/introduction-to-classes/BankAccount.cs#Constructor)] is a property that returns the current date and time. Test this by adding a few deposits and withdrawals in your `Main` method, following the code that creates a new `BankAccount`: @@ -190,7 +190,7 @@ Save the file and type `dotnet run` to try it. To finish this tutorial, you can write the `GetAccountHistory` method that creates a `string` for the transaction history. Add this method to the `BankAccount` type: -[!code-csharp[History](~/samples/snippets/csharp/classes-quickstart/BankAccount.cs#History)] +[!code-csharp[History](./snippets/introduction-to-classes/BankAccount.cs#History)] This 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. diff --git a/samples/snippets/csharp/classes-quickstart/BankAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/BankAccount.cs similarity index 100% rename from samples/snippets/csharp/classes-quickstart/BankAccount.cs rename to docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/BankAccount.cs diff --git a/samples/snippets/csharp/classes-quickstart/Program.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/Program.cs similarity index 100% rename from samples/snippets/csharp/classes-quickstart/Program.cs rename to docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/Program.cs diff --git a/samples/snippets/csharp/classes-quickstart/README.md b/docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/README.md similarity index 100% rename from samples/snippets/csharp/classes-quickstart/README.md rename to docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/README.md diff --git a/samples/snippets/csharp/classes-quickstart/classes.csproj b/docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/classes.csproj similarity index 100% rename from samples/snippets/csharp/classes-quickstart/classes.csproj rename to docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/classes.csproj diff --git a/samples/snippets/csharp/classes-quickstart/transaction.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/transaction.cs similarity index 100% rename from samples/snippets/csharp/classes-quickstart/transaction.cs rename to docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/transaction.cs diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs new file mode 100644 index 0000000000000..c7854de8644bc --- /dev/null +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; + +namespace classes +{ + public class BankAccount + { + public string Number { get; } + public string Owner { get; set; } +#region BalanceComputation + public decimal Balance + { + get + { + decimal balance = 0; + foreach (var item in allTransactions) + { + balance += item.Amount; + } + + return balance; + } + } +#endregion + + private static int accountNumberSeed = 1234567890; +#region Constructor + public BankAccount(string name, decimal initialBalance) + { + this.Number = accountNumberSeed.ToString(); + accountNumberSeed++; + + this.Owner = name; + MakeDeposit(initialBalance, DateTime.Now, "Initial balance"); + } +#endregion + +#region TransactionDeclaration + private List allTransactions = new List(); +#endregion + +#region DepositAndWithdrawal + 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); + } + + public void MakeWithdrawal(decimal amount, DateTime date, string note) + { + if (amount <= 0) + { + throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive"); + } + if (Balance - amount < 0) + { + throw new InvalidOperationException("Not sufficient funds for this withdrawal"); + } + var withdrawal = new Transaction(-amount, date, note); + allTransactions.Add(withdrawal); + } +#endregion + +#region History + 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(); + } +#endregion + } +} diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs new file mode 100644 index 0000000000000..d6bc5e0778c37 --- /dev/null +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs @@ -0,0 +1,42 @@ +using System; + +namespace classes +{ + class Program + { + static void Main(string[] args) + { + 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/tutorials/intro-to-csharp/snippets/object-oriented-programming/README.md b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/README.md new file mode 100644 index 0000000000000..49dfadf9af6af --- /dev/null +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/README.md @@ -0,0 +1,24 @@ +# C# Classes and Objects Sample + +This sample is part of the [Classes and Objects tutorial](https://docs.microsoft.com/dotnet/csharp/tutorials/intro-to-csharp/introduction-to-classes) +for learning C# features. Please see that topic for detailed steps on the code +for this sample. + +## Key Features + +This sample demonstrates introduction of object oriented programming with classes and objects in C#, which explains about creation and usage of classes, objects, constructors, properties and methods. + +## Build and Run + +To build and run the sample, type the following two commands: + +```dotnetcli +dotnet restore +dotnet run +``` + +`dotnet restore` restores the dependencies for this sample. + +`dotnet run` builds the sample and runs the output assembly. + +**Note:** Starting with .NET Core 2.0 SDK, you don't have to run [`dotnet restore`](https://docs.microsoft.com/dotnet/core/tools/dotnet-restore) because it's run implicitly by all commands that require a restore to occur, such as `dotnet new`, `dotnet build` and `dotnet run`. It's still a valid command in certain scenarios where doing an explicit restore makes sense, such as [continuous integration builds in Azure DevOps Services](https://docs.microsoft.com/azure/devops/build-release/apps/aspnet/build-aspnet-core) or in build systems that need to explicitly control the time at which the restore occurs. diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj new file mode 100644 index 0000000000000..c73e0d1692ab3 --- /dev/null +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj @@ -0,0 +1,8 @@ + + + + Exe + netcoreapp3.1 + + + diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/transaction.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/transaction.cs new file mode 100644 index 0000000000000..198418c62dd9e --- /dev/null +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/transaction.cs @@ -0,0 +1,18 @@ +using System; + +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) + { + this.Amount = amount; + this.Date = date; + this.Notes = note; + } + } +} From 2f86b17e7c8e95e743ff20d961cd564df69475ad Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 22 Sep 2020 14:11:38 -0400 Subject: [PATCH 05/16] interim checkin --- .../introduction-to-classes.md | 12 ++++---- .../object-oriented-programming.md | 28 +++++++++++++++++++ .../introduction-to-classes/README.md | 24 ---------------- .../BankAccount.cs | 10 ------- .../object-oriented-programming/README.md | 24 ---------------- 5 files changed, 34 insertions(+), 64 deletions(-) delete mode 100644 docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/README.md delete mode 100644 docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/README.md diff --git a/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md b/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md index 937516fc8cdf9..07a5ef05caec5 100644 --- a/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md +++ b/docs/csharp/tutorials/intro-to-csharp/introduction-to-classes.md @@ -115,11 +115,11 @@ Your bank account class needs to accept deposits and withdrawals to work correct Let's start by creating a new type to represent a transaction. This 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: -[!code-csharp[Transaction](./snippets/introduction-to-classes/Transaction.cs)] +:::code language="csharp" source="./snippets/introduction-to-classes/Transaction.cs"::: Now, let's add a of `Transaction` objects to the `BankAccount` class. Add the following declaration after the constructor in your *BankAccount.cs* file: -[!code-csharp[TransactionDecl](./snippets/introduction-to-classes/BankAccount.cs#TransactionDeclaration)] +:::code language="csharp" source="./snippets/introduction-to-classes/BankAccount.cs" id="TransactionDeclaration"::: The class requires you to import a different namespace. Add the following at the beginning of *BankAccount.cs*: @@ -129,7 +129,7 @@ using System.Collections.Generic; Now, let's change how the `Balance` is reported. It can be found by summing the values of all transactions. Modify the declaration of `Balance` in the `BankAccount` class to the following: -[!code-csharp[BalanceComputation](./snippets/introduction-to-classes/BankAccount.cs#BalanceComputation)] +:::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. @@ -137,13 +137,13 @@ Next, implement the `MakeDeposit` and `MakeWithdrawal` methods. These methods wi This introduces the concept of ***exceptions***. The standard way of indicating that a method cannot 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 is negative. The `MakeWithdrawal` method throws an exception if the withdrawal amount is negative, or if applying the withdrawal results in a negative balance. Add the following code after the declaration of the `allTransactions` list: -[!code-csharp[DepositAndWithdrawal](./snippets/introduction-to-classes/BankAccount.cs#DepositAndWithdrawal)] +:::code language="csharp" source="./snippets/introduction-to-classes/BankAccount.cs" id="DepositAndWithdrawal"::: The [`throw`](../../language-reference/keywords/throw.md) 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 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: -[!code-csharp[Constructor](./snippets/introduction-to-classes/BankAccount.cs#Constructor)] +:::code language="csharp" source="./snippets/introduction-to-classes/BankAccount.cs" id="Constructor"::: is a property that returns the current date and time. Test this by adding a few deposits and withdrawals in your `Main` method, following the code that creates a new `BankAccount`: @@ -190,7 +190,7 @@ Save the file and type `dotnet run` to try it. To finish this tutorial, you can write the `GetAccountHistory` method that creates a `string` for the transaction history. Add this method to the `BankAccount` type: -[!code-csharp[History](./snippets/introduction-to-classes/BankAccount.cs#History)] +:::code language="csharp" source="./snippets/introduction-to-classes/BankAccount.cs" id="History"::: This 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. diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index 484ccff07cf07..2c7ad0092afbf 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -16,6 +16,34 @@ In the preceding tutorial, [introduction to classes](introduction-to-classes.md) In this tutorial, you'll extend that application to make use of *inheritance* and *polymorphism* to add new features. +## 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 kind of bank account, but needs change, and related account types are requested: + +- An interest earning account that accrues at the end of each month. +- A line of credit that can have a negative balance, but when there is 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 down. It can be re-filled 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 would work in the short term, but it would be more work over time. Any changes would need to be replicated across all the affected classes. + +Instead, you can create new bank account types that inherit methods and data from the `BankAccount` class created in the preceding tutorial. These new classes can extend the `BankAccount` class with the specific behavior needed for each type: + +```csharp +public class InterestEarningAccount : BankAccount +{ +} + +public class LineOfCreditAccount : BankAccount +{ +} + +public class GiftCardAccount : BankAccount +{ +} +``` + +Each of these classes will add new behavior + Outline: 1. Create hierarchy of interest earning account, line of credit, fixed loan. diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/README.md b/docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/README.md deleted file mode 100644 index 49dfadf9af6af..0000000000000 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/introduction-to-classes/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# C# Classes and Objects Sample - -This sample is part of the [Classes and Objects tutorial](https://docs.microsoft.com/dotnet/csharp/tutorials/intro-to-csharp/introduction-to-classes) -for learning C# features. Please see that topic for detailed steps on the code -for this sample. - -## Key Features - -This sample demonstrates introduction of object oriented programming with classes and objects in C#, which explains about creation and usage of classes, objects, constructors, properties and methods. - -## Build and Run - -To build and run the sample, type the following two commands: - -```dotnetcli -dotnet restore -dotnet run -``` - -`dotnet restore` restores the dependencies for this sample. - -`dotnet run` builds the sample and runs the output assembly. - -**Note:** Starting with .NET Core 2.0 SDK, you don't have to run [`dotnet restore`](https://docs.microsoft.com/dotnet/core/tools/dotnet-restore) because it's run implicitly by all commands that require a restore to occur, such as `dotnet new`, `dotnet build` and `dotnet run`. It's still a valid command in certain scenarios where doing an explicit restore makes sense, such as [continuous integration builds in Azure DevOps Services](https://docs.microsoft.com/azure/devops/build-release/apps/aspnet/build-aspnet-core) or in build systems that need to explicitly control the time at which the restore occurs. diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs index c7854de8644bc..bff860fc52928 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs @@ -7,7 +7,6 @@ public class BankAccount { public string Number { get; } public string Owner { get; set; } -#region BalanceComputation public decimal Balance { get @@ -21,10 +20,8 @@ public decimal Balance return balance; } } -#endregion private static int accountNumberSeed = 1234567890; -#region Constructor public BankAccount(string name, decimal initialBalance) { this.Number = accountNumberSeed.ToString(); @@ -33,13 +30,9 @@ public BankAccount(string name, decimal initialBalance) this.Owner = name; MakeDeposit(initialBalance, DateTime.Now, "Initial balance"); } -#endregion -#region TransactionDeclaration private List allTransactions = new List(); -#endregion -#region DepositAndWithdrawal public void MakeDeposit(decimal amount, DateTime date, string note) { if (amount <= 0) @@ -63,9 +56,7 @@ public void MakeWithdrawal(decimal amount, DateTime date, string note) var withdrawal = new Transaction(-amount, date, note); allTransactions.Add(withdrawal); } -#endregion -#region History public string GetAccountHistory() { var report = new System.Text.StringBuilder(); @@ -80,6 +71,5 @@ public string GetAccountHistory() return report.ToString(); } -#endregion } } diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/README.md b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/README.md deleted file mode 100644 index 49dfadf9af6af..0000000000000 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# C# Classes and Objects Sample - -This sample is part of the [Classes and Objects tutorial](https://docs.microsoft.com/dotnet/csharp/tutorials/intro-to-csharp/introduction-to-classes) -for learning C# features. Please see that topic for detailed steps on the code -for this sample. - -## Key Features - -This sample demonstrates introduction of object oriented programming with classes and objects in C#, which explains about creation and usage of classes, objects, constructors, properties and methods. - -## Build and Run - -To build and run the sample, type the following two commands: - -```dotnetcli -dotnet restore -dotnet run -``` - -`dotnet restore` restores the dependencies for this sample. - -`dotnet run` builds the sample and runs the output assembly. - -**Note:** Starting with .NET Core 2.0 SDK, you don't have to run [`dotnet restore`](https://docs.microsoft.com/dotnet/core/tools/dotnet-restore) because it's run implicitly by all commands that require a restore to occur, such as `dotnet new`, `dotnet build` and `dotnet run`. It's still a valid command in certain scenarios where doing an explicit restore makes sense, such as [continuous integration builds in Azure DevOps Services](https://docs.microsoft.com/azure/devops/build-release/apps/aspnet/build-aspnet-core) or in build systems that need to explicitly control the time at which the restore occurs. From 554d9e9703bcc89afe9100f707cb8209fe9fa512 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 22 Sep 2020 17:36:15 -0400 Subject: [PATCH 06/16] Update object-oriented-programming.md --- .../tutorials/intro-to-csharp/object-oriented-programming.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index 2c7ad0092afbf..3d096e27d154d 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -42,7 +42,9 @@ public class GiftCardAccount : BankAccount } ``` -Each of these classes will add new behavior +Each of these classes *inherit* the shared behavior from their shared *base class*, the `BankAccount` class. You need to write the implementations for new and different functionality in each of the *derived classes*. + + Outline: From 6dc69d85d12ba3360ea6d06f65d4ba931ce99ff7 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 24 Sep 2020 16:30:02 -0400 Subject: [PATCH 07/16] stashing to fix a bug. --- .../object-oriented-programming.md | 35 ++++++++++++++++++- .../BankAccount.cs | 8 ++++- .../GiftCardAccount.cs | 13 +++++++ .../InterestEarningAccount.cs | 20 +++++++++++ .../LineOfCreditAccount.cs | 10 ++++++ .../object-oriented-programming/Program.cs | 2 +- .../object-oriented-programming.csproj | 2 ++ .../transaction.cs | 2 +- 8 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/GiftCardAccount.cs create mode 100644 docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/InterestEarningAccount.cs create mode 100644 docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index 3d096e27d154d..efacb862e1712 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -42,9 +42,42 @@ public class GiftCardAccount : BankAccount } ``` -Each of these classes *inherit* the shared behavior from their shared *base class*, the `BankAccount` class. You need to write the implementations for new and different functionality in each of the *derived classes*. +Each of these classes *inherit* the shared behavior from their shared *base class*, the `BankAccount` class. You need to write the implementations for new and different functionality in each of the *derived classes*. +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. +When you create the classes as shown in the preceding sample, you'll find that none of your derived classes compile. The `BankAccount` class declares one public constructor with the following signature: + +```csharp +public BankAccount(string name, decimal initialBalance) +``` + +Each derived class must call a base class constructor from its own constructor. In many cases, you don't need to write code for this to work. If you don't write any constructors, the compiler generates a *parameterless constructor*. Any derived class generates a call to the base class's parameterless constructor. In this case, the doesn't generate a parameterless constructor for the `BankAccount` class because it has a constructor defined. In those cases, you must 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 type and names of the base class. 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. + +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. +- 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 is not 0. +- A gift card account: + - Can be refilled once each month, on the last day of the month. + +You can see that all three of these account types have an action that take places at the end of each month. However, each account type does different tasks. You use *polymorphism* to implement this code. You'll create a single `virtual` method in the `BankAccount` class: + +:::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 chose to re-implement. The derived classes use the `override` keyword to define the new implementation. Typically you refer to this as "overridng the base class implementation". You chose the `virtual` keyword and provide an empty implementation because you still want to use the basic `BankAccount` type for some bank accounts. The `BankAccount` class doesn't perform any actions at month end because no actions are needed. Often, creating a `virtual` function that has no implementation should make you consider an `abstract` method instead. If you didn't want to create instances of `BankAccount` objects, you could declare the `PerformMonthEndTransactions` method as `abstract`. You would make the `BankAccount` class `abstract` as well. + + + +. Define the virtual methods (or change some to be virtual) +. Outline: diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs index bff860fc52928..fd6e7401890ac 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace classes +namespace OOProgramming { public class BankAccount { @@ -71,5 +71,11 @@ public string GetAccountHistory() return report.ToString(); } + + // Added for OO tutorial: + + // + public virtual void PerformMonthEndTransactions(decimal? optionalDeposit) { } + // } } diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/GiftCardAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/GiftCardAccount.cs new file mode 100644 index 0000000000000..af44064db4c13 --- /dev/null +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/GiftCardAccount.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OOProgramming +{ + public class GiftCardAccount : BankAccount + { + public GiftCardAccount(string name, decimal initialBalance) : base(name, initialBalance) + { + } + } +} diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/InterestEarningAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/InterestEarningAccount.cs new file mode 100644 index 0000000000000..6ae945f5e047b --- /dev/null +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/InterestEarningAccount.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OOProgramming +{ + public class InterestEarningAccount : BankAccount + { + // + public InterestEarningAccount(string name, decimal initialBalance) : base(name, initialBalance) + { + } + // + + public override void PerformMonthEndTransactions(decimal? optionalDeposit) + { + base.PerformMonthEndTransactions(optionalDeposit); + } + } +} diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs new file mode 100644 index 0000000000000..d2b7a00331c98 --- /dev/null +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace OOProgramming +{ + class LineOfCreditAccount +{ +} +} diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs index d6bc5e0778c37..5e8f0e180ad78 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs @@ -1,6 +1,6 @@ using System; -namespace classes +namespace OOProgramming { class Program { diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj index c73e0d1692ab3..7475237fed609 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj @@ -3,6 +3,8 @@ Exe netcoreapp3.1 + OOProgramming + oo-programming diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/transaction.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/transaction.cs index 198418c62dd9e..945a8ed1373bf 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/transaction.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/transaction.cs @@ -1,6 +1,6 @@ using System; -namespace classes +namespace OOProgramming { public class Transaction { From 260a357eb730d87c5421c01c723e5f63fac97d4e Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 24 Sep 2020 16:35:57 -0400 Subject: [PATCH 08/16] Update object-oriented-programming.md --- .../tutorials/intro-to-csharp/object-oriented-programming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index efacb862e1712..5fe1dbb7acd9c 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -56,7 +56,7 @@ Each derived class must call a base class constructor from its own constructor. :::code language="csharp" source="./snippets/object-oriented-programming/InterestEarningAccount.cs" id="DerivedConstructor"::: -The parameters to this new constructor match the type and names of the base class. 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. +The parameters to this new constructor match the type and names of the base class. 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 these The requirements for the new classes can be stated as follows: From 36f09628eb90032f36356e31d32b582146526fc2 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 25 Sep 2020 14:57:00 -0400 Subject: [PATCH 09/16] interim checkin Need to switch branches and use the windows machine. --- .../object-oriented-programming.md | 21 +++++++++++++++++- .../BankAccount.cs | 2 +- .../GiftCardAccount.cs | 15 ++++++++++++- .../InterestEarningAccount.cs | 10 +++++++-- .../LineOfCreditAccount.cs | 22 ++++++++++++++++--- .../object-oriented-programming/Program.cs | 7 ++++++ 6 files changed, 69 insertions(+), 8 deletions(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index 5fe1dbb7acd9c..112be381e468c 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -66,7 +66,7 @@ The requirements for the new classes can be stated as follows: - 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 is not 0. - A gift card account: - - Can be refilled once each month, on the last day of the month. + - Can be refilled with a specified amount once each month, on the last day of the month. You can see that all three of these account types have an action that take places at the end of each month. However, each account type does different tasks. You use *polymorphism* to implement this code. You'll create a single `virtual` method in the `BankAccount` class: @@ -74,8 +74,27 @@ You can see that all three of these account types have an action that take place 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 chose to re-implement. The derived classes use the `override` keyword to define the new implementation. Typically you refer to this as "overridng the base class implementation". You chose the `virtual` keyword and provide an empty implementation because you still want to use the basic `BankAccount` type for some bank accounts. The `BankAccount` class doesn't perform any actions at month end because no actions are needed. Often, creating a `virtual` function that has no implementation should make you consider an `abstract` method instead. If you didn't want to create instances of `BankAccount` objects, you could declare the `PerformMonthEndTransactions` method as `abstract`. You would make the `BankAccount` class `abstract` as well. +Next, you need to define the implementation for two of the new classes you've created. Start with the `InterestEarningAccount`: +:::code language="csharp" source="./snippets/object-oriented-programming/InterestEarningAccount.cs" id="ApplyMonthendInterest"::: +Add the following code to the `LineOfCreditAccount`. The code negates the balance to compute a positive interest charge: + +:::code language="csharp" source="./snippets/object-oriented-programming/LineOfCreditAccount.cs" id="ApplyMonthendInterest"::: + +The `GiftCardAccount` class needs two changes to implement its month-end functionality. First, modify the constructor to include an optional amount to add each month: + +:::code language="csharp" source="./snippets/object-oriented-programming/GiftCardAccount.cs" id="GiftCardAccountConstruction"::: + +The constructor provides a default value for the `monthlyDeposit` value so callers don't need to provide one for zero. Next, override the `PerformMonthEndTransactions` method to add the monthly deposit, if it was set to a non-zero value in the constructor: + +:::code language="csharp" source="./snippets/object-oriented-programming/GiftCardAccount.cs" id="AddMonthlyDeposit"::: + +The override applies the monthly deposit if it was set in the constructor. + +## Test the implementation + +. it works, except for the line of credit. . Define the virtual methods (or change some to be virtual) . diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs index fd6e7401890ac..b5484e3a50b34 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs @@ -75,7 +75,7 @@ public string GetAccountHistory() // Added for OO tutorial: // - public virtual void PerformMonthEndTransactions(decimal? optionalDeposit) { } + public virtual void PerformMonthEndTransactions() { } // } } diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/GiftCardAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/GiftCardAccount.cs index af44064db4c13..4bb6de539649a 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/GiftCardAccount.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/GiftCardAccount.cs @@ -6,8 +6,21 @@ namespace OOProgramming { public class GiftCardAccount : BankAccount { - public GiftCardAccount(string name, decimal initialBalance) : base(name, initialBalance) + // + private decimal _monthlyDeposit = 0m; + + public GiftCardAccount(string name, decimal initialBalance, decimal monthlyDeposit = 0) : base(name, initialBalance) + => _monthlyDeposit = monthlyDeposit; + // + + // + public override void PerformMonthEndTransactions() { + if (_monthlyDeposit != 0) + { + MakeDeposit(_monthlyDeposit, DateTime.Now, "Add monthly deposit"); + } } + // } } diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/InterestEarningAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/InterestEarningAccount.cs index 6ae945f5e047b..fbfec63ef85ff 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/InterestEarningAccount.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/InterestEarningAccount.cs @@ -12,9 +12,15 @@ public InterestEarningAccount(string name, decimal initialBalance) : base(name, } // - public override void PerformMonthEndTransactions(decimal? optionalDeposit) + // + public override void PerformMonthEndTransactions() { - base.PerformMonthEndTransactions(optionalDeposit); + if (Balance > 500m) + { + var interest = Balance * 0.05m; + MakeDeposit(interest, DateTime.Now, "apply monthly interest"); + } } + // } } diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs index d2b7a00331c98..10af947399b35 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs @@ -4,7 +4,23 @@ namespace OOProgramming { - class LineOfCreditAccount -{ -} + class LineOfCreditAccount : BankAccount + { + public LineOfCreditAccount(string name, decimal initialBalance) : base(name, initialBalance) + { + } + + // + public override void PerformMonthEndTransactions() + { + if (Balance < 0) + { + // Negate the balance to get a positive interest charge: + var interest = -Balance * 0.07m; + MakeWithdrawal(interest, DateTime.Now, "Charge monthly interest"); + + } + } + // + } } diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs index 5e8f0e180ad78..08b85ebd2e245 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs @@ -5,6 +5,13 @@ namespace OOProgramming class Program { static void Main(string[] args) + { + IntroToClasses(); + + var giftCard + } + + private static void IntroToClasses() { var account = new BankAccount("", 1000); Console.WriteLine($"Account {account.Number} was created for {account.Owner} with {account.Balance} balance."); From 78c4a9b26cf7e2dcf16da1db3a3257e367fe099b Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 29 Sep 2020 17:36:27 -0400 Subject: [PATCH 10/16] draft completed. --- .../object-oriented-programming.md | 410 ++---------------- .../BankAccount.cs | 32 +- .../LineOfCreditAccount.cs | 12 +- .../object-oriented-programming/Program.cs | 28 +- .../object-oriented-programming.csproj | 1 + 5 files changed, 111 insertions(+), 372 deletions(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index 112be381e468c..1d275adecceda 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -92,410 +92,92 @@ The constructor provides a default value for the `monthlyDeposit` value so calle The override applies the monthly deposit if it was set in the constructor. -## Test the implementation +Add the following code to the `Main` method to test these changes for the `GiftCardAccount` and the `InterestEarningAccount`: -. it works, except for the line of credit. -. Define the virtual methods (or change some to be virtual) -. +:::code language="csharp" source="./snippets/object-oriented-programming/Program.cs" id="FirstTests"::: -Outline: +Those work fine. Now, add a similar set of test code for the `LineOfCreditAccount`: -1. Create hierarchy of interest earning account, line of credit, fixed loan. -1. Define virtual and abstract methods -1. Implement and run tests +:::code language="csharp" source="./snippets/object-oriented-programming/Program.cs" id="TestLineOfCredit"::: -existing text follows to ensure we don't miss concepts. +When you add the preceding code and run the program, you'll see something like the following error: -## Classes and objects - -The terms *class* describes a *type*. The term*object* describe the *instances* of that type. So, the act of creating an object is called *instantiation*. Using the blueprint analogy, a class is a blueprint, and an object is a building made from that blueprint. - -To define a class: - -```csharp -class SampleClass -{ -} +```console +Unhandled exception. System.ArgumentOutOfRangeException: Amount of deposit must be positive (Parameter 'amount') + at OOProgramming.BankAccount.MakeDeposit(Decimal amount, DateTime date, String note) in BankAccount.cs:line 42 + at OOProgramming.BankAccount..ctor(String name, Decimal initialBalance) in BankAccount.cs:line 31 + at OOProgramming.LineOfCreditAccount..ctor(String name, Decimal initialBalance) in LineOfCreditAccount.cs:line 9 + at OOProgramming.Program.Main(String[] args) in Program.cs:line 29 ``` -C# also provides types called *structures* that are useful when you don't need support for inheritance or polymorphism. For more information, see [Choosing between class and struct](../../../standard/design-guidelines/choosing-between-class-and-struct.md). - -To define a structure: - -```csharp -struct SampleStruct -{ -} -``` +> [!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. -For more information, see the articles on the [class](../../language-reference/keywords/class.md) and [struct](../../language-reference/builtin-types/struct.md) keywords. +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 cannot 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. -### Class members +Let's start by adding a second constructor that includes an optional `minimumBalance` parameter. This new constructor performs all the actions performed by the existing constructor. In addition, 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: -Each class can have different *class members* that include properties that describe class data, methods that define class behavior, and events that provide communication between different classes and objects. + :::code language="csharp" source="./snippets/object-oriented-programming/BankAccount.cs" id="ConstructorModifications"::: -#### Properties and fields +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` cannot 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. -Fields and properties represent information that an object contains. Fields are like variables because they can be read or set directly, subject to applicable access modifiers. +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. -To define a field that can be accessed from within instances of the class: +Now that the `BankAccount` class has a read-only field for the minimum balance, the final change is to change the hard code `0` to `minimumBalance` in the `MakeWithdrawal` method: ```csharp -public class SampleClass -{ - string sampleField; -} +if (Balance - amount < minimumBalance) ``` -Properties have `get` and `set` accessors, which provide more control on how values are set or returned. +After extending the `BankAccount` class, you can modify the `LineOfCreditAccount` constructor to call the new base constructor, as shown in the following code: -C# allows you either to create a private field for storing the property value or use auto-implemented properties that create this field automatically behind the scenes and provide the basic logic for the property procedures. +:::code language="csharp" source="./snippets/object-oriented-programming/LineOfCreditAccount.cs" id="ConstructLineOfCredit"::: -To define an auto-implemented property: +Notice that the `LineOfCreditAccount` constructor changes the sign of the `creditLimit` parameter so it matches the meaning of the `minimumBalance` parameter. -```csharp -class SampleClass -{ - public int SampleProperty { get; set; } -} -``` +## Different overdraft rules -If you need to perform some additional operations for reading and writing the property value, define a field for storing the property value and provide the basic logic for storing and retrieving it: +The last feature to add enables the `LineOfCreditAccount` to charge a fee for going over the credit limit instead of refusing the transaction. -```csharp -class SampleClass -{ - private int _sample; - public int Sample - { - // Return the value stored in a field. - get => _sample; - // Store the value in the field. - set => _sample = value; - } -} -``` - -Most properties have methods or procedures to both set and get the property value. However, you can create read-only or write-only properties to restrict them from being modified or read. In C#, you can omit the `get` or `set` property method. However, auto-implemented properties cannot be write-only. Read-only auto-implemented properties can be set in constructors of the containing class. - -For more information, see: - -- [get](../../language-reference/keywords/get.md) -- [set](../../language-reference/keywords/set.md) - -#### Methods - -A *method* is an action that an object can perform. - -To define a method of a class: +One technique is to define a virtual function where you implement the desired behavior. The `Bank Account` class refactors the `MakeWithdrawal` method into two methods. The new method performs the specified action when the withdrawal takes the balance below the minimum. The existing `MakeWithdrawal` method has the following code: ```csharp -class SampleClass +public void MakeWithdrawal(decimal amount, DateTime date, string note) { - public int SampleMethod(string sampleParam) + if (amount <= 0) { - // Insert code here + throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive"); } -} -``` - -A class can have several implementations, or *overloads*, of the same method that differ in the number of parameters or parameter types. - -To overload a method: - -```csharp -public int SampleMethod(string sampleParam) { } -public int SampleMethod(int sampleParam) { } -``` - -In most cases you declare a method within a class definition. However, C# also supports *extension methods* that allow you to add methods to an existing class outside the actual definition of the class. - -For more information, see: - -- [Methods](../../programming-guide/classes-and-structs/methods.md) -- [Extension Methods](../../programming-guide/classes-and-structs/extension-methods.md) - -#### Constructors - -Constructors are class methods that are executed automatically when an object of a given type is created. Constructors usually initialize the data members of the new object. A constructor can run only once when a class is created. Furthermore, the code in the constructor always runs before any other code in a class. However, you can create multiple constructor overloads in the same way as for any other method. - -To define a constructor for a class: - -```csharp -public class SampleClass -{ - public SampleClass() - { - // Add code here - } -} -``` - -For more information, see [Constructors](../../programming-guide/classes-and-structs/constructors.md). - -#### Finalizers - -A finalizer is used to destruct instances of classes. In .NET, the garbage collector automatically manages the allocation and release of memory for the managed objects in your application. However, you may still need finalizers to clean up any unmanaged resources that your application creates. There can be only one finalizer for a class. - -For more information about finalizers and garbage collection in .NET, see [Garbage Collection](../../../standard/garbage-collection/index.md). - -#### Events - -Events enable a class or object to notify other classes or objects when something of interest occurs. The class that sends (or raises) the event is called the *publisher* and the classes that receive (or handle) the event are called *subscribers*. For more information about events, how they are raised and handled, see [Events](../../../standard/events/index.md). - -- To declare an event in a class, use the [event](../../language-reference/keywords/event.md) keyword. -- To raise an event, invoke the event delegate. -- To subscribe to an event, use the `+=` operator; to unsubscribe from an event, use the `-=` operator. - -#### Nested classes - -A class defined within another class is called *nested*. By default, the nested class is private. - -```csharp -class Container -{ - class Nested + if (Balance - amount < minimumBalance) { - // Add code here. + throw new InvalidOperationException("Not sufficient funds for this withdrawal"); } + var withdrawal = new Transaction(-amount, date, note); + allTransactions.Add(withdrawal); } ``` -To create an instance of the nested class, use the name of the container class followed by the dot and then followed by the name of the nested class: - -```csharp -Container.Nested nestedInstance = new Container.Nested() -``` - -### Access modifiers and access levels +Replace it with the following: -All classes and class members can specify what access level they provide to other classes by using *access modifiers*. +:::code language="csharp" source="./snippets/object-oriented-programming/BankAccount.cs" id="RefactoredMakeWithdrawal"::: -The following access modifiers are available: +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: -| C# Modifier | Definition | -|--|--| -| [public](../../language-reference/keywords/public.md) | The type or member can be accessed by any other code in the same assembly or another assembly that references it. | -| [private](../../language-reference/keywords/private.md) | The type or member can only be accessed by code in the same class. | -| [protected](../../language-reference/keywords/protected.md) | The type or member can only be accessed by code in the same class or in a derived class. | -| [internal](../../language-reference/keywords/internal.md) | The type or member can be accessed by any code in the same assembly, but not from another assembly. | -| [protected internal](../../language-reference/keywords/protected-internal.md) | The type or member can be accessed by any code in the same assembly, or by any derived class in another assembly. | -| [private protected](../../language-reference/keywords/private-protected.md) | The type or member can be accessed by code in the same class or in a derived class within the base class assembly. | +:::code language="csharp" source="./snippets/object-oriented-programming/LineOfCreditAccount.cs" id="AddOverdraftFee"::: -For more information, see [Access Modifiers](../../programming-guide/classes-and-structs/access-modifiers.md). +The override returns a fee transaction when the account is overdrawn. If the withdrawal doesn't go over the limit, the method returns a `null` transaction. That indicates there's no fee. Test these changes by adding the following code to your `Main` method in the `Program` class: -### Instantiating classes - -To create an object, you need to instantiate a class, or create a class instance. - -```csharp -SampleClass sampleObject = new SampleClass(); -``` - -After instantiating a class, you can assign values to the instance's properties and fields and invoke class methods. - -```csharp -// Set a property value. -sampleObject.sampleProperty = "Sample String"; -// Call a method. -sampleObject.SampleMethod(); -``` - -To assign values to properties during the class instantiation process, use object initializers: - -```csharp -// Set a property value. -var sampleObject = new SampleClass -{ - FirstProperty = "A", - SecondProperty = "B" -}; -``` - -For more information, see: - -- [new Operator](../../language-reference/operators/new-operator.md) -- [Object and Collection Initializers](../../programming-guide/classes-and-structs/object-and-collection-initializers.md) - -### Static Classes and Members - -A static member of the class is a property, procedure, or field that is shared by all instances of a class. - -To define a static member: - -```csharp -static class SampleClass -{ - public static string SampleString = "Sample String"; -} -``` - -To access the static member, use the name of the class without creating an object of this class: - -```csharp -Console.WriteLine(SampleClass.SampleString); -``` - -Static classes in C# have static members only and cannot be instantiated. Static members also cannot access non-static properties, fields or methods - -For more information, see: [static](../../language-reference/keywords/static.md). - -### Anonymous types - -Anonymous types enable you to create objects without writing a class definition for the data type. Instead, the compiler generates a class for you. The class has no usable name and contains the properties you specify in declaring the object. - -To create an instance of an anonymous type: - -```csharp -// sampleObject is an instance of a simple anonymous type. -var sampleObject = new -{ - FirstProperty = "A", - SecondProperty = "B" -}; -``` - -For more information, see: [Anonymous Types](../../programming-guide/classes-and-structs/anonymous-types.md). - -## Inheritance - -Inheritance enables you to create a new class that reuses, extends, and modifies the behavior that is defined in another class. The class whose members are inherited is called the *base class*, and the class that inherits those members is called the *derived class*. However, all classes in C# implicitly inherit from the class that supports .NET class hierarchy and provides low-level services to all classes. - -> [!NOTE] -> C# doesn't support multiple inheritance. That is, you can specify only one base class for a derived class. - -To inherit from a base class: - -```csharp -class DerivedClass:BaseClass { } -``` - -By default all classes can be inherited. However, you can specify whether a class must not be used as a base class, or create a class that can be used as a base class only. - -To specify that a class cannot be used as a base class: - -```csharp -public sealed class A { } -``` - -To specify that a class can be used as a base class only and cannot be instantiated: - -```csharp -public abstract class B { } -``` - -For more information, see: - -- [sealed](../../language-reference/keywords/sealed.md) -- [abstract](../../language-reference/keywords/abstract.md) - -### Overriding Members - -By default, a derived class inherits all members from its base class. If you want to change the behavior of the inherited member, you need to override it. That is, you can define a new implementation of the method, property or event in the derived class. - -The following modifiers are used to control how properties and methods are overridden: - -| C# Modifier | Definition | -|--|--| -| [virtual](../../language-reference/keywords/virtual.md) | Allows a class member to be overridden in a derived class. | -| [override](../../language-reference/keywords/override.md) | Overrides a virtual (overridable) member defined in the base class. | -| [abstract](../../language-reference/keywords/abstract.md) | Requires that a class member to be overridden in the derived class. | -| [new Modifier](../../language-reference/keywords/new-modifier.md) | Hides a member inherited from a base class | - -## Interfaces - -Interfaces, like classes, define a set of properties, methods, and events. But unlike classes, interfaces do not provide implementation. They are implemented by classes, and defined as separate entities from classes. An interface represents a contract, in that a class that implements an interface must implement every aspect of that interface exactly as it is defined. - -To define an interface: - -```csharp -interface ISampleInterface -{ - void DoSomething(); -} -``` - -To implement an interface in a class: - -```csharp -class SampleClass : ISampleInterface -{ - void ISampleInterface.DoSomething() - { - // Method implementation. - } -} -``` - -For more information, see the programming guide article on [Interfaces](../../programming-guide/interfaces/index.md) and the language reference article on the [interface](../../language-reference/keywords/interface.md) keyword. - -## Generics - -Classes, structures, interfaces, and methods in .NET can include *type parameters* that define types of objects that they can store or use. The most common example of generics is a collection, where you can specify the type of objects to be stored in a collection. - -To define a generic class: - -```csharp -public class SampleGeneric -{ - public T Field; -} -``` - -To create an instance of a generic class: - -```csharp -var sampleObject = new SampleGeneric(); -sampleObject.Field = "Sample string"; -``` - -For more information, see: - -- [Generics in .NET](../../../standard/generics/index.md) -- [Generics - C# Programming Guide](../../programming-guide/generics/index.md) - -## Delegates - -A *delegate* is a type that defines a method signature, and can provide a reference to any method with a compatible signature. You can invoke (or call) the method through the delegate. Delegates are used to pass methods as arguments to other methods. - -> [!NOTE] -> Event handlers are nothing more than methods that are invoked through delegates. For more information about using delegates in event handling, see [Events](../../../standard/events/index.md). - -To create a delegate: - -```csharp -public delegate void SampleDelegate(string str); -``` - -To create a reference to a method that matches the signature specified by the delegate: - -```csharp -class SampleClass -{ - // Method that matches the SampleDelegate signature. - public static void SampleMethod(string message) - { - // Add code here. - } - - // Method that instantiates the delegate. - void SampleDelegate() - { - SampleDelegate sd = sampleMethod; - sd("Sample string"); - } -} -``` +:::code language="csharp" source="./snippets/object-oriented-programming/Program.cs" id="TestLineOfCredit"::: -For more information, see the programming guide article on [Delegates](../../programming-guide/delegates/index.md) and the language reference article on the [delegate](../../language-reference/builtin-types/reference-types.md) keyword. +Run the program, and check the results. -## Next steps +## Summary -You can learn more about these concepts in these articles: +This tutorial demonstrated many of the techniques used in Object Oriented programming: -- [If and else statement](../../language-reference/keywords/if-else.md) -- [While statement](../../language-reference/keywords/while.md) -- [Do statement](../../language-reference/keywords/do.md) -- [For statement](../../language-reference/keywords/for.md) +- You used *Abstraction* when you kept many details `private` in each class. +- You used *Encapsulation* when you defined classes for each of the different account types. Those described the behavior for that type of account. +- You used *Inheritance* when you leveraged 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. Congratulations, you've finished all our introduction to C# tutorials. If you're eager to learn more, try more of our [tutorials](../index.md). diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs index b5484e3a50b34..cd99e3af442cd 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/BankAccount.cs @@ -22,14 +22,23 @@ public decimal Balance } private static int accountNumberSeed = 1234567890; - public BankAccount(string name, decimal initialBalance) + + // + private readonly decimal minimumBalance; + + public BankAccount(string name, decimal initialBalance) : this(name, initialBalance, 0) { } + + public BankAccount(string name, decimal initialBalance, decimal minimumBalance) { this.Number = accountNumberSeed.ToString(); accountNumberSeed++; this.Owner = name; - MakeDeposit(initialBalance, DateTime.Now, "Initial balance"); + this.minimumBalance = minimumBalance; + if (initialBalance > 0) + MakeDeposit(initialBalance, DateTime.Now, "Initial balance"); } + // private List allTransactions = new List(); @@ -43,19 +52,32 @@ public void MakeDeposit(decimal amount, DateTime date, string note) allTransactions.Add(deposit); } + // public void MakeWithdrawal(decimal amount, DateTime date, string note) { if (amount <= 0) { throw new ArgumentOutOfRangeException(nameof(amount), "Amount of withdrawal must be positive"); } - if (Balance - amount < 0) + var overdraftTransaction = CheckWithdrawalLimit(Balance - amount < minimumBalance); + var withdrawal = new Transaction(-amount, date, note); + allTransactions.Add(withdrawal); + if (overdraftTransaction != null) + allTransactions.Add(overdraftTransaction); + } + + protected virtual Transaction? CheckWithdrawalLimit(bool isOverdrawn) + { + if (isOverdrawn) { throw new InvalidOperationException("Not sufficient funds for this withdrawal"); } - var withdrawal = new Transaction(-amount, date, note); - allTransactions.Add(withdrawal); + else + { + return default; + } } + // public string GetAccountHistory() { diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs index 10af947399b35..c0d8c7a793c69 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/LineOfCreditAccount.cs @@ -6,9 +6,11 @@ namespace OOProgramming { class LineOfCreditAccount : BankAccount { - public LineOfCreditAccount(string name, decimal initialBalance) : base(name, initialBalance) + // + public LineOfCreditAccount(string name, decimal initialBalance, decimal creditLimit) : base(name, initialBalance, -creditLimit) { } + // // public override void PerformMonthEndTransactions() @@ -18,9 +20,15 @@ public override void PerformMonthEndTransactions() // Negate the balance to get a positive interest charge: var interest = -Balance * 0.07m; MakeWithdrawal(interest, DateTime.Now, "Charge monthly interest"); - } } // + + // + protected override Transaction? CheckWithdrawalLimit(bool isOverdrawn) => + isOverdrawn + ? new Transaction(-20, DateTime.Now, "Apply overdraft fee") + : default; + // } } diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs index 08b85ebd2e245..7fc69baf8e516 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/Program.cs @@ -8,7 +8,33 @@ static void Main(string[] args) { IntroToClasses(); - var giftCard + // + 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()); + // } private static void IntroToClasses() diff --git a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj index 7475237fed609..0bcab20846acb 100644 --- a/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj +++ b/docs/csharp/tutorials/intro-to-csharp/snippets/object-oriented-programming/object-oriented-programming.csproj @@ -5,6 +5,7 @@ netcoreapp3.1 OOProgramming oo-programming + enable From e613b2d04b45234c5949803b60c63fe80b960fcc Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 29 Sep 2020 21:07:15 -0400 Subject: [PATCH 11/16] fix lint warning. --- .../tutorials/intro-to-csharp/object-oriented-programming.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index 1d275adecceda..f2fd814133a4d 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -56,9 +56,7 @@ Each derived class must call a base class constructor from its own constructor. :::code language="csharp" source="./snippets/object-oriented-programming/InterestEarningAccount.cs" id="DerivedConstructor"::: -The parameters to this new constructor match the type and names of the base class. 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 these - -The requirements for the new classes can be stated as follows: +The parameters to this new constructor match the type and names of the base class. 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: - An interest earning account: - Will get a credit of 2% of the month-ending-balance. From 0e1245553e33b4131309ee1a68e094cac8312372 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 29 Sep 2020 21:10:49 -0400 Subject: [PATCH 12/16] markdown lint --- .../tutorials/intro-to-csharp/object-oriented-programming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index f2fd814133a4d..8559f8671f38c 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -175,7 +175,7 @@ This tutorial demonstrated many of the techniques used in Object Oriented progra - You used *Abstraction* when you kept many details `private` in each class. - You used *Encapsulation* when you defined classes for each of the different account types. Those described the behavior for that type of account. -- You used *Inheritance* when you leveraged the implementation already created in the `BankAccount` class to save code. +- You used *Inheritance* when you leveraged 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. Congratulations, you've finished all our introduction to C# tutorials. If you're eager to learn more, try more of our [tutorials](../index.md). From 44d731c9088744aff43873376bb5fb592d0c9e34 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 30 Sep 2020 15:36:39 -0400 Subject: [PATCH 13/16] Final proofread. --- .../object-oriented-programming.md | 87 +++++++++---------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index 8559f8671f38c..6a1396ff9c86b 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -1,11 +1,11 @@ --- title: "Object-Oriented Programming (C#)" description: C# provides full support for object-oriented programming including abstraction, encapsulation, inheritance, and polymorphism. -ms.date: 09/16/2020 +ms.date: 09/30/2020 --- # Object-Oriented programming (C#) -C# provides full support for object-oriented programming including abstraction, encapsulation, inheritance, and polymorphism. +C# is, at its core, an object-oriented language. Four of the key techniques used in object-oriented programming are: - *Abstraction* means hiding the unnecessary details from type consumers. - *Encapsulation* means that a group of related properties, methods, and other members are treated as a single unit or object. @@ -14,17 +14,17 @@ C# provides full support for object-oriented programming including abstraction, In the preceding tutorial, [introduction to classes](introduction-to-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. +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. ## 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 kind of bank account, but 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 is only one bank account type. Over time, needs change, and related account types are requested: -- An interest earning account that accrues at the end of each month. -- A line of credit that can have a negative balance, but when there is 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 down. It can be re-filled once at the start of each month. +- 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. -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 would work in the short term, but it would be more work over time. Any changes would need to be replicated across all the affected classes. +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. Instead, you can create new bank account types that inherit methods and data from the `BankAccount` class created in the preceding tutorial. These new classes can extend the `BankAccount` class with the specific behavior needed for each type: @@ -42,61 +42,58 @@ public class GiftCardAccount : BankAccount } ``` -Each of these classes *inherit* the shared behavior from their shared *base class*, the `BankAccount` class. You need to write the implementations for new and different functionality in each of the *derived classes*. +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. +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. The `BankAccount` class declares one public constructor with the following signature: +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: ```csharp public BankAccount(string name, decimal initialBalance) ``` -Each derived class must call a base class constructor from its own constructor. In many cases, you don't need to write code for this to work. If you don't write any constructors, the compiler generates a *parameterless constructor*. Any derived class generates a call to the base class's parameterless constructor. In this case, the doesn't generate a parameterless constructor for the `BankAccount` class because it has a constructor defined. In those cases, you must 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"::: +:::code language="csharp" source="./snippets/object-oriented-programming/InterestEarningAccount.cs" ID="DerivedConstructor"::: -The parameters to this new constructor match the type and names of the base class. 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'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: - An interest earning account: - Will get 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 is not 0. + - 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. - A gift card account: - Can be refilled with a specified amount once each month, on the last day of the month. -You can see that all three of these account types have an action that take places at the end of each month. However, each account type does different tasks. You use *polymorphism* to implement this code. You'll create a single `virtual` method in the `BankAccount` class: +You can see that all three of these account types have an action that takes places at the end of each month. However, each account type does different tasks. You use *polymorphism* to implement this code. Create a single `virtual` method in the `BankAccount` class: -:::code language="csharp" source="./snippets/object-oriented-programming/BankAccount.cs" id="DeclareMonthEndTransactions"::: +:::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 chose to re-implement. The derived classes use the `override` keyword to define the new implementation. Typically you refer to this as "overridng the base class implementation". You chose the `virtual` keyword and provide an empty implementation because you still want to use the basic `BankAccount` type for some bank accounts. The `BankAccount` class doesn't perform any actions at month end because no actions are needed. Often, creating a `virtual` function that has no implementation should make you consider an `abstract` method instead. If you didn't want to create instances of `BankAccount` objects, you could declare the `PerformMonthEndTransactions` method as `abstract`. You would make the `BankAccount` class `abstract` as well. +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`: -Next, you need to define the implementation for two of the new classes you've created. Start with the `InterestEarningAccount`: +:::code language="csharp" source="./snippets/object-oriented-programming/InterestEarningAccount.cs" ID="ApplyMonthendInterest"::: -:::code language="csharp" source="./snippets/object-oriented-programming/InterestEarningAccount.cs" id="ApplyMonthendInterest"::: +Add the following code to the `LineOfCreditAccount`. The code negates the balance to compute a positive interest charge that is withdrawn from the account: -Add the following code to the `LineOfCreditAccount`. The code negates the balance to compute a positive interest charge: - -:::code language="csharp" source="./snippets/object-oriented-programming/LineOfCreditAccount.cs" id="ApplyMonthendInterest"::: +:::code language="csharp" source="./snippets/object-oriented-programming/LineOfCreditAccount.cs" ID="ApplyMonthendInterest"::: The `GiftCardAccount` class needs two changes to implement its month-end functionality. First, modify the constructor to include an optional amount to add each month: -:::code language="csharp" source="./snippets/object-oriented-programming/GiftCardAccount.cs" id="GiftCardAccountConstruction"::: - -The constructor provides a default value for the `monthlyDeposit` value so callers don't need to provide one for zero. Next, override the `PerformMonthEndTransactions` method to add the monthly deposit, if it was set to a non-zero value in the constructor: +:::code language="csharp" source="./snippets/object-oriented-programming/GiftCardAccount.cs" ID="GiftCardAccountConstruction"::: -:::code language="csharp" source="./snippets/object-oriented-programming/GiftCardAccount.cs" id="AddMonthlyDeposit"::: +The constructor provides a default value for the `monthlyDeposit` value so callers can omit a `0` for no monthly deposit. Next, override the `PerformMonthEndTransactions` method to add the monthly deposit, if it was set to a non-zero value in the constructor: -The override applies the monthly deposit if it was set in the constructor. +:::code language="csharp" source="./snippets/object-oriented-programming/GiftCardAccount.cs" ID="AddMonthlyDeposit"::: -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 to test these changes for the `GiftCardAccount` and the `InterestEarningAccount`: -:::code language="csharp" source="./snippets/object-oriented-programming/Program.cs" id="FirstTests"::: +:::code language="csharp" source="./snippets/object-oriented-programming/Program.cs" ID="FirstTests"::: -Those work fine. Now, add a similar set of test code for the `LineOfCreditAccount`: +Verify the results. Now, add a similar set of test code for the `LineOfCreditAccount`: -:::code language="csharp" source="./snippets/object-oriented-programming/Program.cs" id="TestLineOfCredit"::: +:::code language="csharp" source="./snippets/object-oriented-programming/Program.cs" ID="TestLineOfCredit"::: When you add the preceding code and run the program, you'll see something like the following error: @@ -111,13 +108,13 @@ 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. -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 cannot 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 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. -Let's start by adding a second constructor that includes an optional `minimumBalance` parameter. This new constructor performs all the actions performed by the existing constructor. In addition, 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 additional field: - :::code language="csharp" source="./snippets/object-oriented-programming/BankAccount.cs" id="ConstructorModifications"::: + :::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` cannot 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 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. 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. @@ -129,7 +126,7 @@ if (Balance - amount < minimumBalance) After extending the `BankAccount` class, you can modify the `LineOfCreditAccount` constructor to call the new base constructor, as shown in the following code: -:::code language="csharp" source="./snippets/object-oriented-programming/LineOfCreditAccount.cs" id="ConstructLineOfCredit"::: +:::code language="csharp" source="./snippets/object-oriented-programming/LineOfCreditAccount.cs" ID="ConstructLineOfCredit"::: Notice that the `LineOfCreditAccount` constructor changes the sign of the `creditLimit` parameter so it matches the meaning of the `minimumBalance` parameter. @@ -137,7 +134,7 @@ Notice that the `LineOfCreditAccount` constructor changes the sign of the `credi The last feature to add enables the `LineOfCreditAccount` to charge a fee for going over the credit limit instead of refusing the transaction. -One technique is to define a virtual function where you implement the desired behavior. The `Bank Account` class refactors the `MakeWithdrawal` method into two methods. The new method performs the specified action when the withdrawal takes the balance below the minimum. The existing `MakeWithdrawal` method has the following code: +One technique is to define a virtual function where you implement the required behavior. The `Bank Account` class refactors the `MakeWithdrawal` method into two methods. The new method does the specified action when the withdrawal takes the balance below the minimum. The existing `MakeWithdrawal` method has the following code: ```csharp public void MakeWithdrawal(decimal amount, DateTime date, string note) @@ -155,26 +152,26 @@ public void MakeWithdrawal(decimal amount, DateTime date, string note) } ``` -Replace it with the following: +Replace it with the following code: -:::code language="csharp" source="./snippets/object-oriented-programming/BankAccount.cs" id="RefactoredMakeWithdrawal"::: +:::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 , 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: -:::code language="csharp" source="./snippets/object-oriented-programming/LineOfCreditAccount.cs" id="AddOverdraftFee"::: +:::code language="csharp" source="./snippets/object-oriented-programming/LineOfCreditAccount.cs" ID="AddOverdraftFee"::: The override returns a fee transaction when the account is overdrawn. If the withdrawal doesn't go over the limit, the method returns a `null` transaction. That indicates there's no fee. Test these changes by adding the following code to your `Main` method in the `Program` class: -:::code language="csharp" source="./snippets/object-oriented-programming/Program.cs" id="TestLineOfCredit"::: +:::code language="csharp" source="./snippets/object-oriented-programming/Program.cs" ID="TestLineOfCredit"::: Run the program, and check the results. ## Summary -This tutorial demonstrated many of the techniques used in Object Oriented programming: +This tutorial demonstrated many of the techniques used in Object-Oriented programming: - You used *Abstraction* when you kept many details `private` in each class. -- You used *Encapsulation* when you defined classes for each of the different account types. Those described the behavior for that type of account. +- You used *Encapsulation* when you defined classes for each of the different account types. Those classes described the behavior for that type of account. - You used *Inheritance* when you leveraged 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. From 1d5c65a1c727ad90be572b156cde595cdf3dc2de Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 30 Sep 2020 15:54:43 -0400 Subject: [PATCH 14/16] add missing emphasis marker --- .../tutorials/intro-to-csharp/object-oriented-programming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index 6a1396ff9c86b..0421d0e2ab996 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -42,7 +42,7 @@ 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*. From d34664842c736aa73525e28d9424f7378a8c6839 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 30 Sep 2020 20:50:50 -0400 Subject: [PATCH 15/16] Apply suggestions from code review Co-authored-by: Tom Dykstra --- .../tutorials/intro-to-csharp/object-oriented-programming.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index 0421d0e2ab996..f288f7e6f8ad4 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -5,7 +5,7 @@ ms.date: 09/30/2020 --- # Object-Oriented programming (C#) -C# is, at its core, an object-oriented language. Four of the key techniques used in object-oriented programming are: +C# is an object-oriented language. Four of the key techniques used in object-oriented programming are: - *Abstraction* means hiding the unnecessary details from type consumers. - *Encapsulation* means that a group of related properties, methods, and other members are treated as a single unit or object. @@ -175,4 +175,4 @@ This tutorial demonstrated many of the techniques used in Object-Oriented progra - You used *Inheritance* when you leveraged 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. -Congratulations, you've finished all our introduction to C# tutorials. If you're eager to learn more, try more of our [tutorials](../index.md). +Congratulations, you've finished all our introduction to C# tutorials. To learn more, try more of our [tutorials](../index.md). From 70f24fd3763a079b77faedc5a0e32cde672c42be Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 30 Sep 2020 20:52:03 -0400 Subject: [PATCH 16/16] Update object-oriented-programming.md --- .../tutorials/intro-to-csharp/object-oriented-programming.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md index f288f7e6f8ad4..b7916154cf45f 100644 --- a/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md +++ b/docs/csharp/tutorials/intro-to-csharp/object-oriented-programming.md @@ -175,4 +175,4 @@ This tutorial demonstrated many of the techniques used in Object-Oriented progra - You used *Inheritance* when you leveraged 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. -Congratulations, you've finished all our introduction to C# tutorials. To learn more, try more of our [tutorials](../index.md). +Congratulations, you've finished all of our introduction to C# tutorials. To learn more, try more of our [tutorials](../index.md).