Implement base and derived classes
Class inheritance is a fundamental concept in object-oriented programming that allows you to develop a hierarchy of classes that share common properties and methods. Inheritance enables you to define a base class with common properties and methods that can be shared by derived classes. Derived classes inherit the members of the base class and can extend or modify them to create specialized classes. In this exercise, you create a base class and derived classes to represent different types of bank accounts.
In this exercise, you create three derived classes that inherit from the BankAccount
base class: CheckingAccount
, SavingsAccount
, and MoneyMarketAccount
. Each derived class will have include an instance constructor that accesses the base class constructor. You explore the use of abstract
, virtual
, sealed
, new
, and override
keywords to implement specialized behavior in the derived classes. You update the Program.cs file to demonstrate the extended features implemented in the derived classes.
This exercise takes approximately 30 minutes to complete.
Before you start
Before you can start this exercise, you need to:
-
Ensure that you have the latest short term support (STS) version of the .NET SDK installed on your computer. You can download the latest versions of the .NET SDK using the following URL: Download .NET
-
Ensure that you have Visual Studio Code installed on your computer. You can download Visual Studio Code using the following URL: Download Visual Studio Code
-
Ensure that you have the C# Dev Kit configured in Visual Studio Code.
For additional help configuring the Visual Studio Code environment, see Install and configure Visual Studio Code for C# development
Exercise scenario
Suppose you’re helping a non-profit company with a software project. You’ve decided to sharpen your object-oriented programming skills by creating a simple banking app. You’ve developed an initial version of the app that includes the following files:
-
AccountCalculations.cs: This file contains static utility methods for various financial calculations, such as compound interest, transaction fees, overdraft fees, and currency exchange.
-
BankAccount.cs: This file defines the BankAccount class, implementing the IBankAccount interface and including properties, constructors, methods for account operations, and a copy constructor.
-
BankCustomer.cs: This file defines the BankCustomer class, with properties for first name, last name, and a unique customer ID. It includes methods to return the full name, update the customer’s name, and display customer information. It also maintains a static property for the next customer ID.
-
BankCustomerMethods.cs: This file implements the methods defined in the IBankCustomer interface for the BankCustomer class, including methods to return full name, update name, and display customer information.
-
Extensions.cs: This file contains extension methods for IBankCustomer and IBankAccount interfaces, providing additional functionality such as validation and greeting. For BankCustomer, it includes methods to check if the customer ID is valid and to greet the customer. For BankAccount, it includes methods to check if the account is overdrawn and if a specified amount can be withdrawn.
-
IBankAccount.cs: This file defines the IBankAccount interface, specifying the properties and methods that a bank account class must implement.
-
IBankCustomer.cs: This file defines the IBankCustomer interface, specifying the properties and methods that a bank customer class must implement.
-
Program.cs: This file contains the main entry point of the application, demonstrating the creation and manipulation of BankCustomer and BankAccount objects, as well as various operations like deposits, withdrawals, and transfers.
This exercise includes the following tasks:
-
Review the current version of your banking app.
-
Create the
CheckingAccount
,SavingsAccount
, andMoneyMarketAccount
derived classes. -
Update the
BankAccount
class definition withabstract
andsealed
keywords. -
Implement specialized features in derived classes using properties and constructors.
-
Use the
new
keyword to hide base class methods in derived classes. -
Override methods and properties in derived classes.
-
Use the
base
keyword in overridden methods to access base class members.
Review the current version of your banking app
In this task, you download the existing version of your banking app and review the code.
Use the following steps to complete this section of the exercise:
-
Download the starter code from the following URL: Implement inheritance and polymorphism - exercise code projects
-
Extract the contents of the LP3SampleApps.zip file to a folder location on your computer.
-
Expand the LP3SampleApps folder, and then open the
Reuse_M1
folder.The Reuse_M1 folder contains the following code project folders:
- Solution
- Starter
The Starter folder contains the starter project files for this exercise.
-
Use Visual Studio Code to open the Starter folder.
-
In the EXPLORER view, collapse the STARTER folder, select SOLUTION EXPLORER, and expand the Reuse_M1 project.
You should see the following project files:
- AccountCalculations.cs
- BankAccount.cs
- BankCustomer.cs
- BankCustomerMethods.cs
- Extensions.cs
- IBankAccount.cs
- IBankCustomer.cs
- Program.cs
-
Take a few minutes to open and review each of the files.
-
AccountCalculations.cs
: This file contains static utility methods for various financial calculations, such as compound interest, transaction fees, overdraft fees, and currency exchange. -
BankAccount.cs
: This file defines the BankAccount class, implementing the IBankAccount interface and including properties, constructors, methods for account operations, and a copy constructor. -
BankCustomer.cs
: This file defines the BankCustomer class, implementing the IBankCustomer interface and including properties, constructors, and a copy constructor. -
BankCustomerMethods.cs
: This file implements the methods defined in the IBankCustomer interface for the BankCustomer class, including methods to return full name, update name, and display customer information. -
Extensions.cs
: This file contains extension methods for IBankCustomer and IBankAccount interfaces, providing additional functionality such as validation and greeting. -
IBankAccount.cs
: This file defines the IBankAccount interface, specifying the properties and methods that a bank account class must implement. -
IBankCustomer.cs
: This file defines the IBankCustomer interface, specifying the properties and methods that a bank customer class must implement. -
Program.cs
: This file contains the main entry point of the application, demonstrating the creation and manipulation of BankCustomer and BankAccount objects, as well as various operations like deposits, withdrawals, and transfers.
-
-
Run the app and review the output in the terminal window.
To run your app, right-click the Reuse_M1 project in the Solution Explorer, select Debug, and then select Start New Instance.
Your app should produce output that’s similar to the following example:
Creating BankCustomer objects... BankCustomer 1: Tim Shao 0012761163 BankCustomer 2: Lisa Shao 0012761164 BankCustomer 3: Sandy Zoeng 0012761165 Creating BankAccount objects for customers... Account 1: Account # 13847819, type Checking, balance 200, rate 0, customer ID 0012761163 Account 2: Account # 13847820, type Checking, balance 1500, rate 0, customer ID 0012761164 Account 3: Account # 13847821, type Checking, balance 2500, rate 0, customer ID 0012761165 Updating BankCustomer 1's name... Updated BankCustomer 1: Thomas Margand 0012761163 Demonstrating BankAccount methods... Depositing $500.00 into Account 1... Account 1 after deposit: Balance = $700.00 Withdrawing $200.00 from Account 2... Account 2 after withdrawal: Balance = $1,300.00, Withdrawal successful: True Transferring $300.00 from Account 3 to Account 1... Account 3 after transfer: Balance = $2,200.00, Transfer successful: True Account 1 after receiving transfer: Balance = $1,000.00 Demonstrating utility methods in AccountCalculations... Compound interest on account1 balance of $1,000.00 at 0.00% for 1 year: $0.00 Is account number 13847819 valid? True Transaction fee for a $800.00 wire transfer at a 1.00% rate and a max fee of $10.00 is: $8.00 Overdraft fee for an account that's $500.00 overdrawn, using a penalty rate of 5.00% and a max fee of $10.00 is: $10.00 The foreign currency received after exchanging $100.00 at an exchange rate of 1.20 is: $120.00 Applying interest to Account 1... Account 1 after applying interest: Balance = $1,000.00, Interest Rate = 0.00%, Time Period = 0.08 years Issue cashier's check from account 2... Account 2 after requesting cashier's check: Received cashier's check: True, Balance = $795.00, Transaction Amount = $500.00, Transaction Fee Rate = 1.00%, Max Transaction Fee = $10.00 Applying refund to Account 3... Account 3 after applying refund: Balance = $2,250.00, Refund Amount = $50.00 Demonstrating extension methods... Hello, Thomas Margand! Is customer1 ID valid? True Can account2 withdraw 2000? False Is account3 overdrawn? False Displaying customer and account information... Customer ID: 0012761163, Name: Thomas Margand Account Number: 13847819, Type: Checking, Balance: 1000, Interest Rate: 0, Customer ID: 0012761163 Demonstrating named and optional parameters... Account Number: 13847822, Type: Checking, Balance: 200, Interest Rate: 0, Customer ID: 0012761163 Account Number: 13847823, Type: Checking, Balance: 1500, Interest Rate: 0, Customer ID: 0012761163 Account Number: 13847824, Type: Checking, Balance: 200, Interest Rate: 0, Customer ID: 0012761164 Account Number: 13847825, Type: Checking, Balance: 5000, Interest Rate: 0, Customer ID: 0012761164 Demonstrating object initializers and copy constructors... BankCustomer 4: Mikaela Lee 0012761166 BankCustomer 5 (copy of customer4): Mikaela Lee 0012761167 Account 8: Account # 13847826, type Savings, balance 200, rate 0, customer ID 0012761166 Account 9 (copy of account8): Account # 13847827, type Savings, balance 200, rate 0, customer ID 0012761166
The customer IDs and account numbers in your output will be different from the example output. Remember that they’re sequential values based on a randomly generated initial value.
Create the CheckingAccount, SavingsAccount, and MoneyMarketAccount derived classes
In this task, you create derived classes that inherit from the BankAccount
base class. You’ll create three derived classes: CheckingAccount
, SavingsAccount
, and MoneyMarketAccount
. Each derived class will have a simple constructor that accesses the base class constructor. You’ll update the Program.cs file to demonstrate instantiating the derived classes and inheriting base class properties and methods.
Use the following steps to complete this section of the exercise:
-
Create a new class named
CheckingAccount
.You should see the following code in the new file:
using System; namespace Reuse_M1; public class CheckingAccount { }
You’ll configure
CheckingAccount
to inherit fromBankAccount
. -
Update the class definition to specify that
CheckingAccount
inherits fromBankAccount
.For example:
public class CheckingAccount : BankAccount { }
The
CheckingAccount : BankAccount
syntax indicates thatCheckingAccount
inherits fromBankAccount
. -
Update the
CheckingAccount
class with a constructor that accesses the base class constructor.For example:
public class CheckingAccount : BankAccount { public CheckingAccount(string customerId, double initialBalance) : base(customerId, initialBalance, "Checking" ) { } }
Notice that the
CheckingAccount
constructor calls the base class constructor using thebase
keyword. The constructor takes two parameters:customerId
andinitialBalance
. It passes these parameters to the base class constructor along with a string containing the account type, which is “Checking”. -
Create derived classes for
SavingsAccount
andMoneyMarketAccount
.The SavingsAccount.cs file should contain the following code:
using System; namespace Reuse_M1; public class SavingsAccount : BankAccount { public SavingsAccount(string customerId, double initialBalance) : base(customerId, initialBalance, "Savings") { } }
The MoneyMarketAccount.cs file should contain the following code:
using System; namespace Reuse_M1; public class MoneyMarketAccount : BankAccount { public MoneyMarketAccount(string customerId, double initialBalance) : base(customerId, initialBalance, "Money Market") { } }
-
Open the Program.cs file.
-
Replace the contents of the Program.cs file with the following code snippet:
using Reuse_M1; using System.Globalization; class Program { static void Main() { // Create BankCustomer objects string firstName = "Tim"; string lastName = "Shao"; Console.WriteLine($"Creating a BankCustomer object for customer {firstName} {lastName}..."); BankCustomer customer1 = new BankCustomer(firstName, lastName); // Task 1: Review the code files in the Starter project // Task 2: Create derived classes (CheckingAccount, SavingsAccount, and MoneyMarketAccount) } }
The Program class uses
Main
as the entry point of the application and creates aBankCustomer
object for a customer named Tim Shao. -
To demonstrate instantiating the derived classes, add the following code to the end of
Main
:// Step 1 - Create account objects using the base and derived classes - BankAccount, CheckingAccount, SavingsAccount, and MoneyMarketAccount Console.WriteLine($"\nUsing derived classes to create bank account objects for {customer1.ReturnFullName()}..."); BankAccount bankAccount1 = new BankAccount(customer1.CustomerId, 1000, "Checking"); CheckingAccount checkingAccount1 = new CheckingAccount(customer1.CustomerId, 500); SavingsAccount savingsAccount1 = new SavingsAccount(customer1.CustomerId, 1000); MoneyMarketAccount moneyMarketAccount1 = new MoneyMarketAccount(customer1.CustomerId, 2000);
Notice that objects are created using the base and derived classes.
-
To demonstrate inheritance of base class properties and methods, add the following code to the end of
Main
:// Step 2 - Demonstrate using inherited properties to display account information Console.WriteLine($"\nUsing inherited properties to display {customer1.ReturnFullName()}'s account information..."); Console.WriteLine($" - {checkingAccount1.AccountType} account #{checkingAccount1.AccountNumber} has a balance of {checkingAccount1.Balance.ToString("C", CultureInfo.CurrentCulture)}"); Console.WriteLine($" - {savingsAccount1.AccountType} account #{savingsAccount1.AccountNumber} has a balance of {savingsAccount1.Balance.ToString("C", CultureInfo.CurrentCulture)}"); Console.WriteLine($" - {moneyMarketAccount1.AccountType} account #{moneyMarketAccount1.AccountNumber} has a balance of {moneyMarketAccount1.Balance.ToString("C", CultureInfo.CurrentCulture)}");
-
Run the app and verify that the following output is generated:
Creating a BankCustomer object for customer Tim Shao... Using derived classes to create bank account objects for Tim Shao... Using inherited properties to display Tim Shao's account information... - Checking account #10027975 has a balance of $1,000.00 - Checking account #10027976 has a balance of $500.00 - Savings account #10027977 has a balance of $1,000.00 - Money Market account #10027978 has a balance of $2,000.00
This output demonstrates the following:
- Bank account objects can be created using base and derived classes.
- The base class properties can be accessed from within the derived classes.
-
To demonstrate inheritance of the base class methods, add the following code to
Main
:// Step 3 - Demonstrate using inherited methods to withdraw, transfer, and deposit funds Console.WriteLine("\nDemonstrating inheritance of the Withdraw, Transfer, and Deposit methods from the base class..."); // define a transaction amount double transactionAmount = 200; // Withdraw from checking account, transfer from savings account to checking account, and deposit into money market account Console.WriteLine($" - Withdraw {transactionAmount} from {checkingAccount1.AccountType} account"); checkingAccount1.Withdraw(transactionAmount); Console.WriteLine($" - Transfer {transactionAmount.ToString("C", CultureInfo.CurrentCulture)} from {savingsAccount1.AccountType} account into {checkingAccount1.AccountType} account"); savingsAccount1.Transfer(checkingAccount1, transactionAmount); Console.WriteLine($" - Deposit {transactionAmount.ToString("C", CultureInfo.CurrentCulture)} into {moneyMarketAccount1.AccountType} account"); moneyMarketAccount1.Deposit(transactionAmount); Console.WriteLine($" - {checkingAccount1.AccountType} account #{checkingAccount1.AccountNumber} has a balance of {checkingAccount1.Balance.ToString("C", CultureInfo.CurrentCulture)}"); Console.WriteLine($" - {savingsAccount1.AccountType} account #{savingsAccount1.AccountNumber} has a balance of {savingsAccount1.Balance.ToString("C", CultureInfo.CurrentCulture)}"); Console.WriteLine($" - {moneyMarketAccount1.AccountType} account #{moneyMarketAccount1.AccountNumber} has a balance of {moneyMarketAccount1.Balance.ToString("C", CultureInfo.CurrentCulture)}");
-
Run the app and verify that the following output is generated:
Creating a BankCustomer object for customer Tim Shao... Using derived classes to create bank account objects for Tim Shao... Using inherited properties to display Tim Shao's account information... - Checking account #13358753 has a balance of $500.00 - Savings account #13358754 has a balance of $1,000.00 - Money Market account #13358755 has a balance of $2,000.00 Demonstrating inheritance of the Withdraw, Transfer, and Deposit methods from the base class... - Withdraw 200 from Checking account - Transfer $200.00 from Savings account into Checking account - Deposit $200.00 into Money Market account - Checking account #13358753 has a balance of $500.00 - Savings account #13358754 has a balance of $800.00 - Money Market account #13358755 has a balance of $2,200.00
The additional output demonstrates the following:
- Base class methods are accessible from instances of a derived class.
Update the BankAccount class definition with abstract and sealed keywords
In this task, you update the base class definition using abstract
and sealed
keywords and you observe the effect on instantiating base and derived classes.
Use the following steps to complete this section of the exercise:
-
Open the BankAccount.cs file.
-
Update the definition for
BankAccount
to include theabstract
keyword.public abstract class BankAccount : IBankAccount
-
Build the
Starter
solution. -
Notice the error message that appears in the terminal window.
error CS0144: Cannot create an instance of the abstract type or interface 'BankAccount'
This error message reminds you that classes defined using the
abstract
keyword can’t be directly instantiated. -
Open the Program.cs file.
-
Comment out the two code lines that include references to the BankAccount class.
// Step 1 - Create account objects using the base and derived classes - BankAccount, CheckingAccount, SavingsAccount, and MoneyMarketAccount Console.WriteLine($"\nUsing derived classes to create bank account objects for {customer1.ReturnFullName()}..."); //BankAccount bankAccount1 = new BankAccount(customer1.CustomerId, 1000, "Checking"); CheckingAccount checkingAccount1 = new CheckingAccount(customer1.CustomerId, 500); SavingsAccount savingsAccount1 = new SavingsAccount(customer1.CustomerId, 1000); MoneyMarketAccount moneyMarketAccount1 = new MoneyMarketAccount(customer1.CustomerId, 2000); // Step 2 - Demonstrate using inherited properties to display account information Console.WriteLine($"\nUsing inherited properties to display {customer1.ReturnFullName()}'s account information..."); //Console.WriteLine($" - {bankAccount1.AccountType} account #{bankAccount1.AccountNumber} has a balance of {bankAccount1.Balance.ToString("C", CultureInfo.CurrentCulture)}");
-
Run the app and verify that the following output is generated:
Creating a BankCustomer object for customer Tim Shao... Using derived classes to create bank account objects for Tim Shao... Using inherited properties to display Tim Shao's account information... - Checking account #13358753 has a balance of $500.00 - Savings account #13358754 has a balance of $1,000.00 - Money Market account #13358755 has a balance of $2,000.00 Demonstrating inheritance of the Withdraw, Transfer, and Deposit methods from the base class... - Withdraw 200 from Checking account - Transfer $200.00 from Savings account into Checking account - Deposit $200.00 into Money Market account - Checking account #13358753 has a balance of $500.00 - Savings account #13358754 has a balance of $800.00 - Money Market account #13358755 has a balance of $2,200.00
Defining
BankAccount
as an abstract class prevents it from being instantiated directly. The derived classes can still be instantiated and inherit the properties and methods of the base class. -
Open the BankAccount.cs file.
-
Update the definition for
BankAccount
to include thesealed
keyword.public sealed class BankAccount : IBankAccount
-
Build the
Starter
solution. -
Notice the error messages that appear in the terminal window.
Attempting to instantiate a derived class using a sealed base class generates an error. For example:
error CS0509: 'CheckingAccount': cannot derive from sealed type 'BankAccount'
Classes that are defined using the
sealed
keyword can’t be used as base classes for other classes. -
Update the BankAccount class definition to remove the
sealed
keyword.public class BankAccount : IBankAccount
Implement specialized features in derived classes using properties and constructors
Derived classes can extend the functionality of a base class by adding new properties and constructors that are specific to the derived class. For example, the CheckingAccount
, SavingsAccount
, and MoneyMarketAccount
classes extend BankAccount
by with features that are specific to their account type:
CheckingAccount
initializes an InterestRate of 0.0 percent, a limit on the overdraft amount, and an overdraft fee that’s applied to the account if the balance drops below zero.SavingsAccount
initializes an InterestRate of 2.0 percent, a limit on the number of withdrawals per month, and a minimum opening balance requirement.MoneyMarketAccount
initializes an InterestRate of 4.0 percent, a minimum balance requirement, and a minimum opening balance requirement.
In this task, you extend base class functionality by adding feature specific properties and constructors to the derived classes.
Use the following steps to complete this section of the exercise:
-
Open the
CheckingAccount
class.CheckingAccount
extends theBankAccount
base class with the following features:- A public static property named
DefaultOverdraftLimit
that’s used to set a overdraft limit for checking accounts (a negative balance limit). - A public static property named
DefaultInterestRate
that’s used to set the interest rate for checking accounts. - A public property named
OverdraftLimit
that’s used to get and set the overdraft limit for a checking account. The property should be initialized to the default overdraft limit. Premium customers receive a higher overdraft limit and no overdraft fee.
- A public static property named
-
Update the
CheckingAccount
class with the following properties:// public static properties with private setters for default overdraft limit and default interest rate public static double DefaultOverdraftLimit { get; private set; } public static double DefaultInterestRate { get; private set; } // public property for overdraft limit public double OverdraftLimit { get; set; }
The private setters for the
DefaultOverdraftLimit
andDefaultInterestRate
properties ensure that the values can only be set within the class. -
Update the
CheckingAccount
class with a static constructor that initializes theDefaultOverdraftLimit
andDefaultInterestRate
properties.static CheckingAccount() { DefaultOverdraftLimit = 500; DefaultInterestRate = 0.0; }
Static constructors are called automatically before the first instance of a class is created or any static members are referenced. Static read-only properties can be initialized in the static constructor.
-
Update the
CheckingAccount
class constructor to include anoverdraftLimit
parameter that’s used to initialize theOverdraftLimit
property.public CheckingAccount(string customerIdNumber, double balance = 200, double overdraftLimit = 500) : base(customerIdNumber, balance, "Checking") { OverdraftLimit = overdraftLimit; }
The updated constructor takes three parameters:
customerIdNumber
,balance
, andoverdraftLimit
. It initializes theOverdraftLimit
property with theoverdraftLimit
parameter value. -
Open the
SavingsAccount
class.SavingsAccount
extends theBankAccount
base class with the following features:- A public static property named
DefaultWithdrawalLimit
that’s used to set a limit on the number of withdrawals per month. - A public static property named
DefaultMinimumOpeningBalance
that’s used to set the minimum opening balance requirement for a savings account. - A public static property named
DefaultInterestRate
that’s used to set the interest rate for savings accounts. - A public property named
WithdrawalLimit
that’s used to get and set the withdrawal limit for a savings account. The property should be initialized to the default withdrawal limit. - A public property named
MinimumOpeningBalance
that’s used to get and set the minimum opening balance requirement for a savings account. The property should be initialized to the default minimum opening balance.
- A public static property named
-
Update the
SavingsAccount
class with the following fields and properties:// private field to track the number of withdrawals this month private int _withdrawalsThisMonth; // public static properties with private setters for default withdrawal limit, default minimum opening balance, and default interest rate public static int DefaultWithdrawalLimit { get; private set; } public static double DefaultMinimumOpeningBalance { get; private set; } public static double DefaultInterestRate { get; private set; } // public property for withdrawal limit and minimum opening balance public int WithdrawalLimit { get; set; } public double MinimumOpeningBalance { get; set; }
Notice the following about these fields and properties:
- The
_withdrawalsThisMonth
field tracks the number of withdrawals made from the account in the current month. - The
DefaultWithdrawalLimit
,DefaultMinimumOpeningBalance
, andDefaultInterestRate
properties have a private setter that ensures the values can only be set within the class. - The
WithdrawalLimit
andMinimumOpeningBalance
properties are public.
- The
-
Update the
SavingsAccount
class with a static constructor that initializes theDefaultWithdrawalLimit
,DefaultMinimumOpeningBalance
, andDefaultInterestRate
properties.static SavingsAccount() { DefaultWithdrawalLimit = 6; DefaultMinimumOpeningBalance = 500; DefaultInterestRate = 0.02; // 2 percent interest rate }
-
Update the
SavingsAccount
class with a constructor that enforces a minimum opening balance and sets a limit on the number of withdrawals per month.public SavingsAccount(string customerIdNumber, double balance = 500, int withdrawalLimit = 6) : base(customerIdNumber, balance, "Savings") { if (balance < DefaultMinimumOpeningBalance) { throw new ArgumentException($"Balance must be at least {DefaultMinimumOpeningBalance}"); } WithdrawalLimit = withdrawalLimit; _withdrawalsThisMonth = 0; MinimumOpeningBalance = DefaultMinimumOpeningBalance; // Set the minimum opening balance to the default value }
Notice that the constructor takes three parameters:
customerIdNumber
,balance
, andwithdrawalLimit
. It initializes theWithdrawalLimit
property with thewithdrawalLimit
parameter value. The constructor also enforces a minimum opening balance requirement. -
Open the
MoneyMarketAccount
class.MoneyMarketAccount
extends theBankAccount
base class with the following features:- A public static property named
DefaultInterestRate
that’s used to set the interest rate for money market accounts. - A public static property named
DefaultMinimumBalance
that’s used to set the minimum balance requirement for a money market account. - A public static property named
DefaultMinimumOpeningBalance
that’s used to set the minimum opening balance requirement for a money market account. - A public property named
MinimumBalance
that’s used to get and set the minimum balance requirement for a money market account. The property should be initialized to the default minimum balance. - A public property named
MinimumOpeningBalance
that’s used to get and set the minimum opening balance requirement for a money market account. The property should be initialized to the default minimum opening balance.
- A public static property named
-
Update the
MoneyMarketAccount
class with the following properties:// public static properties with private setters for default interest rate, default minimum balance, and default minimum opening balance public static double DefaultInterestRate { get; private set; } public static double DefaultMinimumBalance { get; private set; } public static double DefaultMinimumOpeningBalance { get; private set; } // public property for minimum balance and minimum opening balance public double MinimumBalance { get; set; } public double MinimumOpeningBalance { get; set; }
-
Update the
MoneyMarketAccount
class with a static constructor that initializes theDefaultInterestRate
,DefaultMinimumBalance
, andDefaultMinimumOpeningBalance
properties.static MoneyMarketAccount() { DefaultInterestRate = 0.04; // 4 percent interest rate DefaultMinimumBalance = 1000; // Default minimum balance DefaultMinimumOpeningBalance = 2000; // Default minimum opening balance }
-
Update the
MoneyMarketAccount
class with a constructor that enforces a minimum balance requirement.public MoneyMarketAccount(string customerIdNumber, double balance = 2000, double minimumBalance = 1000) : base(customerIdNumber, balance, "Money Market") { if (balance < DefaultMinimumOpeningBalance) { throw new ArgumentException($"Balance must be at least {DefaultMinimumOpeningBalance}"); } MinimumBalance = minimumBalance; MinimumOpeningBalance = DefaultMinimumOpeningBalance; // Set the minimum opening balance to the default value }
The constructor takes three parameters:
customerIdNumber
,balance
, andminimumBalance
. It initializes theMinimumBalance
property with theminimumBalance
parameter value. The constructor also enforces a minimum balance requirement. -
Build and run the app to verify that the previous output is still generated:
Creating a BankCustomer object for customer Tim Shao... Using derived classes to create bank account objects for Tim Shao... Using inherited properties to display Tim Shao's account information... - Checking account #13358753 has a balance of $500.00 - Savings account #13358754 has a balance of $1,000.00 - Money Market account #13358755 has a balance of $2,000.00 Demonstrating inheritance of the Withdraw, Transfer, and Deposit methods from the base class... - Withdraw 200 from Checking account - Transfer $200.00 from Savings account into Checking account - Deposit $200.00 into Money Market account - Checking account #13358753 has a balance of $500.00 - Savings account #13358754 has a balance of $800.00 - Money Market account #13358755 has a balance of $2,200.00
You’ll update the Program.cs file later in this exercise to demonstrate the use of the new properties and constructors.
Use the new keyword to hide base class methods in derived classes
Derived classes can extend the behavior of a base class by defining a method that has the same name and signature as a base class method. The new
keyword is used to indicate that the method is intended to hide the base class method.
In this task, you examine the use of the new
keyword when extending the behavior of a base class method.
Use the following steps to complete this section of the exercise:
-
Open the CheckingAccount.cs file.
-
To duplicate the base class’s
DisplayAccountInfo
method in the derived class, add the following code to the end of theCheckingAccount
class:public string DisplayAccountInfo() { return $"Account Number: {AccountNumber}, Type: {AccountType}, Balance: {Balance}, Interest Rate: {InterestRate}, Customer ID: {CustomerId}"; }
This method is an exact duplicate of the
DisplayAccountInfo
method in the base class. -
Hover the mouse pointer over the
DisplayAccountInfo
method inCheckingAccount
.When you hover the mouse pointer over the
DisplayAccountInfo
method, you see a warning that indicates the method hides the inherited memberBankAccount.DisplayAccountInfo()
. This warning is generated because the method has the same name and signature as the base class method. -
Use the following code to customize the method’s return value and apply the
new
keyword:public new string DisplayAccountInfo() { return $"Account Number: {AccountNumber}, Type: {AccountType}, Balance: {Balance:C}, Interest Rate: {InterestRate}, Customer ID: {CustomerId} - Checking Accounts have an overdraft limit of {DefaultOverdraftLimit:C}"; }
The
DisplayAccountInfo
method inCheckingAccount
now returns information specific to theCheckingAccount
class. Thenew
keyword clarifies your intension to hide the base class method. Since your intension is clear, there’s no longer a warning message about hiding the base class method.The
C
format specifier is used to format currency values in the string that’s returned. -
Open the Program.cs file.
-
Add the following code to the bottom of the
Main
method:// Step 4 - Demonstrate using the 'new' keyword to override a base class method Console.WriteLine("\nDemonstrating the use of the 'new' keyword to override a base class method..."); // Display account information for each account Console.WriteLine($" - {checkingAccount1.DisplayAccountInfo()}"); Console.WriteLine($" - {savingsAccount1.DisplayAccountInfo()}"); Console.WriteLine($" - {moneyMarketAccount1.DisplayAccountInfo()}");
Notice that the
DisplayAccountInfo
method is called on each of the derived classes. TheCheckingAccount
classes uses the newDisplayAccountInfo
method that hides the base class method, while theSavingsAccount
andMoneyMarketAccount
classes are still using the base class method. -
Run the app and verify that the following output is generated:
Creating a BankCustomer object for customer Tim Shao... Using derived classes to create bank account objects for Tim Shao... Using inherited properties to display Tim Shao's account information... - Checking account #10866248 has a balance of $500.00 - Savings account #10866249 has a balance of $1,000.00 - Money Market account #10866250 has a balance of $2,000.00 Demonstrating inheritance of the Withdraw, Transfer, and Deposit methods from the base class... - Withdraw 200 from Checking account - Transfer $200.00 from Savings account into Checking account - Deposit $200.00 into Money Market account - Checking account #10866248 has a balance of $500.00 - Savings account #10866249 has a balance of $800.00 - Money Market account #10866250 has a balance of $2,200.00 Demonstrating the use of the 'new' keyword to override a base class method... - Account Number: 10866248, Type: Checking, Balance: $500.00, Interest Rate: 0, Customer ID: 0010986337 - Checking Accounts have an overdraft limit of $500.00 - Account Number: 10866249, Type: Savings, Balance: 800, Interest Rate: 0, Customer ID: 0010986337 - Account Number: 10866250, Type: Money Market, Balance: 2200, Interest Rate: 0, Customer ID: 0010986337
Override properties and methods in derived classes
Derived classes can modify the behavior of a base class by overriding base class properties and methods. For example, you can override the InterestRate
property and the Withdraw
method in the CheckingAccount
, SavingsAccount
, and MoneyMarketAccount
classes to implement specialized behavior in your derived classes.
In this task, you override properties and methods in the derived classes to implement specialized behavior.
Use the following steps to complete this section of the exercise:
-
Open the BankAccount.cs file.
-
Use the following code to change the
InterestRate
property fromstatic
tovirtual
:public virtual double InterestRate { get; protected set; } // Virtual property to allow overriding in derived classes
The
virtual
keyword allows theInterestRate
property to be overridden in derived classes. Theprotected
access modifier allows the property to beset
within the derived classes. -
To remove the code that initializes the
InterestRate
property in the base class, update the static constructor as follows:static BankAccount() { Random random = new Random(); s_nextAccountNumber = random.Next(10000000, 20000000); TransactionRate = 0.01; // Default transaction rate for wire transfers and cashier's checks MaxTransactionFee = 10; // Maximum transaction fee for wire transfers and cashier's checks OverdraftRate = 0.05; // Default penalty rate for an overdrawn checking account (negative balance) MaxOverdraftFee = 10; // Maximum overdraft fee for an overdrawn checking account }
-
Use the following code to the make the
Withdraw
methodvirtual
:public virtual bool Withdraw(double amount) { if (amount > 0 && Balance >= amount) { Balance -= amount; return true; } return false; }
The
virtual
keyword allows theWithdraw
method to be overridden in derived classes. -
Use the following code to make the
DisplayAccountInfo
methodvirtual
:public virtual string DisplayAccountInfo() { return $"Account Number: {AccountNumber}, Type: {AccountType}, Balance: {Balance}, Interest Rate: {InterestRate}, Customer ID: {CustomerId}"; }
-
Open the CheckingAccount.cs file.
-
Use the following code to override the
Withdraw
method:public override bool Withdraw(double amount) { if (amount > 0 && Balance + OverdraftLimit >= amount) { Balance -= amount; // Check if the account is overdrawn if (Balance < 0) { double overdraftFee = AccountCalculations.CalculateOverdraftFee(Math.Abs(Balance), BankAccount.OverdraftRate, BankAccount.MaxOverdraftFee); Balance -= overdraftFee; Console.WriteLine($"Overdraft fee of ${overdraftFee} applied."); } return true; } return false; }
Notice that the
Withdraw
method in theCheckingAccount
class uses theOverdraftLimit
property to determine how far below zero the account can be overdrawn. If the account is withdrawal is allowed and the account is overdrawn, an overdraft fee is applied to the balance. -
Open the SavingsAccount.cs file.
-
Use the following code to override the
Withdraw
method:public override bool Withdraw(double amount) { if (amount > 0 && Balance >= amount && _withdrawalsThisMonth < WithdrawalLimit) { Balance -= amount; _withdrawalsThisMonth++; return true; } return false; }
Notice that the
Withdraw
method in theSavingsAccount
class uses the_withdrawalsThisMonth
field to track the number of withdrawals made from the account in the current month. If the number of withdrawals exceeds theWithdrawalLimit
, the method returnsfalse
. -
Use the following code to add a method that resets the
_withdrawalsThisMonth
field:public void ResetWithdrawalLimit() { _withdrawalsThisMonth = 0; }
This method can be used to reset the
_withdrawalsThisMonth
field to zero. For example, the method can be called at the beginning of each month to reset the withdrawal count. -
Open the MoneyMarketAccount.cs file.
-
Use the following code to override the
Withdraw
method:public override bool Withdraw(double amount) { if (amount > 0 && Balance - amount >= MinimumBalance) { Balance -= amount; return true; } return false; }
Notice that the
Withdraw
method in theMoneyMarketAccount
class enforces a minimum balance requirement. -
Use the following code to override the
InterestRate
property in each of the derived classes:public override double InterestRate { get { return DefaultInterestRate; } protected set { DefaultInterestRate = value; } }
Each of the derived classes initializes a static
DefaultInterestRate
value that’s used by the overriddenInterestRate
property.- Checking – interest rate is 0.0%
- Savings – interest rate is 2.0%
- MoneyMarket - interest rate is 4.0%
-
Open the Program.cs file.
-
To demonstrate the overridden properties and methods, add the following code to the bottom of the
Main
method://Step 5: Demonstrate the overridden properties and methods in the derived classes Console.WriteLine("\nDemonstrating specialized Withdraw behavior:"); // CheckingAccount: Withdraw within overdraft limit Console.WriteLine("\nCheckingAccount: Attempting to withdraw $600 (within overdraft limit)..."); checkingAccount1.Withdraw(600); Console.WriteLine(checkingAccount1.DisplayAccountInfo()); // CheckingAccount: Withdraw exceeding overdraft limit Console.WriteLine("\nCheckingAccount: Attempting to withdraw $1000 (exceeding overdraft limit)..."); checkingAccount1.Withdraw(1000); Console.WriteLine(checkingAccount1.DisplayAccountInfo()); // SavingsAccount: Withdraw within limit Console.WriteLine("\nSavingsAccount: Attempting to withdraw $200 (within withdrawal limit)..."); savingsAccount1.Withdraw(200); Console.WriteLine(savingsAccount1.DisplayAccountInfo()); // SavingsAccount: Withdraw exceeding limit Console.WriteLine("\nSavingsAccount: Attempting to withdraw $900 (exceeding withdrawal limit)..."); savingsAccount1.Withdraw(900); Console.WriteLine(savingsAccount1.DisplayAccountInfo()); // SavingsAccount: Exceeding maximum number of withdrawals per month Console.WriteLine("\nSavingsAccount: Exceeding maximum number of withdrawals per month..."); savingsAccount1.ResetWithdrawalLimit(); for (int i = 0; i < 7; i++) { Console.WriteLine($"Attempting to withdraw $50 (withdrawal {i + 1})..."); savingsAccount1.Withdraw(50); Console.WriteLine(savingsAccount1.DisplayAccountInfo()); } // MoneyMarketAccount: Withdraw within minimum balance Console.WriteLine("\nMoneyMarketAccount: Attempting to withdraw $1000 (maintaining minimum balance)..."); moneyMarketAccount1.Withdraw(1000); Console.WriteLine(moneyMarketAccount1.DisplayAccountInfo()); // MoneyMarketAccount: Withdraw exceeding minimum balance Console.WriteLine("\nMoneyMarketAccount: Attempting to withdraw $1900 (exceeding minimum balance)..."); moneyMarketAccount1.Withdraw(1900); Console.WriteLine(moneyMarketAccount1.DisplayAccountInfo());
Notice that this code tests the overridden
Withdraw
method in each of the derived classes:- The
CheckingAccount
class demonstrates overdraft behavior and fees. - The
SavingsAccount
class enforces a positive balance and demonstrates the limited number of monthly withdrawals - The
MoneyMarketAccount
class demonstrates the minimum balance requirements.
- The
-
Run the app and verify that the following output is generated:
Creating a BankCustomer object for customer Tim Shao... Using derived classes to create bank account objects for Tim Shao... Using inherited properties to display Tim Shao's account information... - Checking account #12406201 has a balance of $500.00 - Savings account #12406202 has a balance of $1,000.00 - Money Market account #12406203 has a balance of $2,000.00 Demonstrating inheritance of the Withdraw, Transfer, and Deposit methods from the base class... - Withdraw 200 from Checking account - Transfer $200.00 from Savings account into Checking account - Deposit $200.00 into Money Market account - Checking account #12406201 has a balance of $500.00 - Savings account #12406202 has a balance of $800.00 - Money Market account #12406203 has a balance of $2,200.00 Demonstrating the use of the 'new' keyword to override a base class method... - Account Number: 12406201, Type: Checking, Balance: $500.00, Interest Rate: 0, Customer ID: 0010876989 - Checking Accounts have an overdraft limit of $500.00 - Account Number: 12406202, Type: Savings, Balance: 800, Interest Rate: 0.02, Customer ID: 0010876989 - Account Number: 12406203, Type: Money Market, Balance: 2200, Interest Rate: 0.04, Customer ID: 0010876989 Demonstrating specialized Withdraw behavior: CheckingAccount: Attempting to withdraw $600 (within overdraft limit)... Overdraft fee of $5 applied. Account Number: 12406201, Type: Checking, Balance: ($105.00), Interest Rate: 0, Customer ID: 0010876989 - Checking Accounts have an overdraft limit of $500.00 CheckingAccount: Attempting to withdraw $1000 (exceeding overdraft limit)... Account Number: 12406201, Type: Checking, Balance: ($105.00), Interest Rate: 0, Customer ID: 0010876989 - Checking Accounts have an overdraft limit of $500.00 SavingsAccount: Attempting to withdraw $200 (within withdrawal limit)... Account Number: 12406202, Type: Savings, Balance: 600, Interest Rate: 0.02, Customer ID: 0010876989 SavingsAccount: Attempting to withdraw $900 (exceeding withdrawal limit)... Account Number: 12406202, Type: Savings, Balance: 600, Interest Rate: 0.02, Customer ID: 0010876989 SavingsAccount: Exceeding maximum number of withdrawals per month... Attempting to withdraw $50 (withdrawal 1)... Account Number: 12406202, Type: Savings, Balance: 550, Interest Rate: 0.02, Customer ID: 0010876989 Attempting to withdraw $50 (withdrawal 2)... Account Number: 12406202, Type: Savings, Balance: 500, Interest Rate: 0.02, Customer ID: 0010876989 Attempting to withdraw $50 (withdrawal 3)... Account Number: 12406202, Type: Savings, Balance: 450, Interest Rate: 0.02, Customer ID: 0010876989 Attempting to withdraw $50 (withdrawal 4)... Account Number: 12406202, Type: Savings, Balance: 400, Interest Rate: 0.02, Customer ID: 0010876989 Attempting to withdraw $50 (withdrawal 5)... Account Number: 12406202, Type: Savings, Balance: 350, Interest Rate: 0.02, Customer ID: 0010876989 Attempting to withdraw $50 (withdrawal 6)... Account Number: 12406202, Type: Savings, Balance: 300, Interest Rate: 0.02, Customer ID: 0010876989 Attempting to withdraw $50 (withdrawal 7)... Account Number: 12406202, Type: Savings, Balance: 300, Interest Rate: 0.02, Customer ID: 0010876989 MoneyMarketAccount: Attempting to withdraw $1000 (maintaining minimum balance)... Account Number: 12406203, Type: Money Market, Balance: 1200, Interest Rate: 0.04, Customer ID: 0010876989 MoneyMarketAccount: Attempting to withdraw $1900 (exceeding minimum balance)... Account Number: 12406203, Type: Money Market, Balance: 1200, Interest Rate: 0.04, Customer ID: 0010876989
Use the base keyword in overridden methods to access base class members
In this task, you use the base
keyword in overridden methods to access base class members.
Use the following steps to complete this section of the exercise:
-
Open the BankAccount.cs file.
-
Use the following code to update the
DisplayAccountInfo
method with thevirtual
keyword:public virtual string DisplayAccountInfo() { return $"Account Number: {AccountNumber}, Type: {AccountType}, Balance: {Balance:C}, Interest Rate: {InterestRate:P}, Customer ID: {CustomerId}"; }
The
virtual
keyword allows theDisplayAccountInfo
method to be overridden in derived classes. Currency and percentage values are formatted using theC
andP
format specifiers. -
Open the CheckingAccount.cs file.
-
Use the following code to override the
DisplayAccountInfo
method and usebase
keyword to access the base class’sDisplayAccountInfo
method:public override string DisplayAccountInfo() { return base.DisplayAccountInfo() + $", Overdraft Limit: {OverdraftLimit}"; }
Using the
base
keyword enables you to reuse the base class’sDisplayAccountInfo
method, which is a best practice. The method appends information specific to theCheckingAccount
class to the string returned by the base class method. -
Open the SavingsAccount.cs file.
-
Use the following code to override and extend the base class’s
DisplayAccountInfo
method inside theSavingsAccount
class:public override string DisplayAccountInfo() { return base.DisplayAccountInfo() + $", Withdrawal Limit: {WithdrawalLimit}, Withdrawals This Month: {_withdrawalsThisMonth}"; }
The
DisplayAccountInfo
method in theSavingsAccount
class uses thebase
keyword to access the base class’sDisplayAccountInfo
method. The method then appends information specific to theSavingsAccount
class. -
Open the MoneyMarketAccount.cs file.
-
Use the following code to override and extend the base class’s
DisplayAccountInfo
method inside theMoneyMarketAccount
class:public override string DisplayAccountInfo() { return base.DisplayAccountInfo() + $", Minimum Balance: {MinimumBalance}, Minimum Opening Balance: {MinimumOpeningBalance}"; }
The
DisplayAccountInfo
method in theMoneyMarketAccount
class uses thebase
keyword to access the base class’sDisplayAccountInfo
method. The method then appends information specific to theMoneyMarketAccount
class. -
Run the app and verify that the following output is generated:
Creating a BankCustomer object for customer Tim Shao... Using derived classes to create bank account objects for Tim Shao... Using inherited properties to display Tim Shao's account information... - Checking account #12552069 has a balance of $500.00 - Savings account #12552070 has a balance of $1,000.00 - Money Market account #12552071 has a balance of $2,000.00 Demonstrating inheritance of the Withdraw, Transfer, and Deposit methods from the base class... - Withdraw 200 from Checking account - Transfer $200.00 from Savings account into Checking account - Deposit $200.00 into Money Market account - Checking account #12552069 has a balance of $500.00 - Savings account #12552070 has a balance of $800.00 - Money Market account #12552071 has a balance of $2,200.00 Demonstrating the use of the 'new' keyword to override a base class method... - Account Number: 12552069, Type: Checking, Balance: $500.00, Interest Rate: 0.00%, Customer ID: 0013536499, Overdraft Limit: 500 - Account Number: 12552070, Type: Savings, Balance: $800.00, Interest Rate: 2.00%, Customer ID: 0013536499, Withdrawal Limit: 6, Withdrawals This Month: 1 - Account Number: 12552071, Type: Money Market, Balance: $2,200.00, Interest Rate: 4.00%, Customer ID: 0013536499, Minimum Balance: 1000, Minimum Opening Balance: 2000 Demonstrating specialized Withdraw behavior: CheckingAccount: Attempting to withdraw $600 (within overdraft limit)... Overdraft fee of $5 applied. Account Number: 12552069, Type: Checking, Balance: ($105.00), Interest Rate: 0.00%, Customer ID: 0013536499, Overdraft Limit: 500 CheckingAccount: Attempting to withdraw $1000 (exceeding overdraft limit)... Account Number: 12552069, Type: Checking, Balance: ($105.00), Interest Rate: 0.00%, Customer ID: 0013536499, Overdraft Limit: 500 SavingsAccount: Attempting to withdraw $200 (within withdrawal limit)... Account Number: 12552070, Type: Savings, Balance: $600.00, Interest Rate: 2.00%, Customer ID: 0013536499, Withdrawal Limit: 6, Withdrawals This Month: 2 SavingsAccount: Attempting to withdraw $900 (exceeding withdrawal limit)... Account Number: 12552070, Type: Savings, Balance: $600.00, Interest Rate: 2.00%, Customer ID: 0013536499, Withdrawal Limit: 6, Withdrawals This Month: 2 SavingsAccount: Exceeding maximum number of withdrawals per month... Attempting to withdraw $50 (withdrawal 1)... Account Number: 12552070, Type: Savings, Balance: $550.00, Interest Rate: 2.00%, Customer ID: 0013536499, Withdrawal Limit: 6, Withdrawals This Month: 1 Attempting to withdraw $50 (withdrawal 2)... Account Number: 12552070, Type: Savings, Balance: $500.00, Interest Rate: 2.00%, Customer ID: 0013536499, Withdrawal Limit: 6, Withdrawals This Month: 2 Attempting to withdraw $50 (withdrawal 3)... Account Number: 12552070, Type: Savings, Balance: $450.00, Interest Rate: 2.00%, Customer ID: 0013536499, Withdrawal Limit: 6, Withdrawals This Month: 3 Attempting to withdraw $50 (withdrawal 4)... Account Number: 12552070, Type: Savings, Balance: $400.00, Interest Rate: 2.00%, Customer ID: 0013536499, Withdrawal Limit: 6, Withdrawals This Month: 4 Attempting to withdraw $50 (withdrawal 5)... Account Number: 12552070, Type: Savings, Balance: $350.00, Interest Rate: 2.00%, Customer ID: 0013536499, Withdrawal Limit: 6, Withdrawals This Month: 5 Attempting to withdraw $50 (withdrawal 6)... Account Number: 12552070, Type: Savings, Balance: $300.00, Interest Rate: 2.00%, Customer ID: 0013536499, Withdrawal Limit: 6, Withdrawals This Month: 6 Attempting to withdraw $50 (withdrawal 7)... Account Number: 12552070, Type: Savings, Balance: $300.00, Interest Rate: 2.00%, Customer ID: 0013536499, Withdrawal Limit: 6, Withdrawals This Month: 6 MoneyMarketAccount: Attempting to withdraw $1000 (maintaining minimum balance)... Account Number: 12552071, Type: Money Market, Balance: $1,200.00, Interest Rate: 4.00%, Customer ID: 0013536499, Minimum Balance: 1000, Minimum Opening Balance: 2000 MoneyMarketAccount: Attempting to withdraw $1900 (exceeding minimum balance)... Account Number: 12552071, Type: Money Market, Balance: $1,200.00, Interest Rate: 4.00%, Customer ID: 0013536499, Minimum Balance: 1000, Minimum Opening Balance: 2000
Clean up
Now that you’ve finished the exercise, consider archiving your project files for review at a later time. Having your own projects available for review can be a valuable resource when you’re learning to code. Also, building up a portfolio of projects can be a great way to demonstrate your skills to potential employers.