Update a class with properties and methods
Classes use properties and methods to encapsulate data and behavior. Properties define the data that a class contains, and methods define the behavior that the class performs.
In this exercise, you update an existing code project by developing properties and methods.
This exercise takes approximately 35 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:
- BankCustomer.cs: The BankCustomer class includes fields for first name, last name, and customer ID. The class also includes constructors that initialize the fields.
- BankAccount.cs: The BankAccount class includes fields for account number, balance, interest rate, account type, and customer ID. The class also includes constructors that initialize the fields.
- Program.cs: The Program.cs file includes code that creates instances of the BankCustomer and BankAccount classes and demonstrates how each class is used.
This exercise includes the following tasks:
- Review the current version of your banking app
- Create properties for the BankCustomer class
- Create automatically implemented properties for the BankAccount class
- Create read-only properties for the BankAccount class
- Create methods for the BankCustomer and BankAccount classes
- Create extension methods for the BankCustomer and BankAccount classes
- Update the Program.cs file to demonstrate the updated classes, properties, and methods
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 classes, properties, and methods - exercise code projects
-
Extract the contents of the LP1SampleApps.zip file to a folder location on your computer.
-
Expand the LP1SampleApps folder, and then open the
Classes_M2folder.The Classes_M2 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 Classes_M2 project.
You should see the following project files:
- BankAccount.cs
- BankCustomer.cs
- Program.cs
-
Open the BankCustomer.cs file.
-
Take a minute to review the BankCustomer class.
using System; namespace Classes_M2; public class BankCustomer { private static int s_nextCustomerId; public string FirstName = "Tim"; public string LastName = "Shao"; public readonly string CustomerId; static BankCustomer() { Random random = new Random(); s_nextCustomerId = random.Next(10000000, 20000000); } public BankCustomer(string firstName, string lastName) { FirstName = firstName; LastName = lastName; this.CustomerId = (s_nextCustomerId++).ToString("D10"); } }The
BankCustomerclass includes fields forFirstName,LastName,CustomerId, and a static fields_nextCustomerId. Thes_nextCustomerIdfield is used to generate a unique customer ID for each customer.The
BankCustomerclass also includes two constructors. The first constructor is a static constructor that initializes thes_nextCustomerIdfield with a random number eight-digit integer. The second constructor takes two parameters,firstNameandlastName, and initializes theFirstNameandLastNamefields with the values of the parameters. The constructor also incrementss_nextCustomerIdand uses the incremented value to assign a unique value toCustomerId.[!NOTE] The
thiskeyword refers to the current instance of the class. It’s used to access fields, properties, and methods of the current instance. In theBankCustomerclass, thethiskeyword is used to access the read-onlyCustomerIdfield. Thethiskeyword is not required in this context, but it’s used for clarity. Thethiskeyword is not available in a static constructor. -
Open the BankAccount.cs file.
-
Take a minute to review the BankAccount class.
using System; namespace Classes_M2; public class BankAccount { private static int s_nextAccountNumber; public readonly int AccountNumber; public double Balance = 0; public static double InterestRate; public string AccountType = "Checking"; public readonly string CustomerId; static BankAccount() { Random random = new Random(); s_nextAccountNumber = random.Next(10000000, 20000000); InterestRate = 0; } public BankAccount(string customerIdNumber) { this.AccountNumber = s_nextAccountNumber++; this.CustomerId = customerIdNumber; } public BankAccount(string customerIdNumber, double balance, string accountType) { this.AccountNumber = s_nextAccountNumber++; this.CustomerId = customerIdNumber; this.Balance = balance; this.AccountType = accountType; } }The
BankAccountclass includes fields forAccountNumber,Balance,InterestRate,AccountType, andCustomerId. TheAccountNumberfield is read-only and is initialized in the instance constructors. TheBalancefield is read-write and can be changed at any time. TheAccountTypefield is read-write and can be changed at any time. TheCustomerIdfield is read-only and is initialized in the instance constructors. TheBankAccountclass also has a static fields_nextAccountNumberwhich is used to generate unique account numbers for each new account. This field is initialized in a static constructor, which is called only once when the class is first loaded. The static constructor uses theRandomclass to generate a random starting value fors_nextAccountNumber. Additionally, the static constructor initializes the static fieldInterestRateto 0.The class also includes two instance constructors. The first instance constructor takes a single parameter,
customerIdNumber, and initializes theAccountNumberandCustomerIdfields. The second instance constructor takes three parameters:customerIdNumber,balance, andaccountType. This constructor initializes theAccountNumber,CustomerId,Balance, andAccountTypefields based on the provided values. Both constructors increment thes_nextAccountNumberto ensure that each new account has a unique account number. The static constructor initializes thes_nextAccountNumberandInterestRatefields. -
Open the Program.cs file.
-
Take a minute to review the code.
using Classes_M2; string firstName = "Tim"; string lastName = "Shao"; BankCustomer customer1 = new BankCustomer(firstName, lastName); firstName = "Lisa"; BankCustomer customer2 = new BankCustomer(firstName, lastName); firstName = "Sandy"; lastName = "Zoeng"; BankCustomer customer3 = new BankCustomer(firstName, lastName); Console.WriteLine($"BankCustomer 1: {customer1.FirstName} {customer1.LastName} {customer1.CustomerId}"); Console.WriteLine($"BankCustomer 2: {customer2.FirstName} {customer2.LastName} {customer2.CustomerId}"); Console.WriteLine($"BankCustomer 3: {customer3.FirstName} {customer3.LastName} {customer3.CustomerId}"); // Create accounts for customers BankAccount account1 = new BankAccount(customer1.CustomerId); BankAccount account2 = new BankAccount(customer2.CustomerId, 1500, "Checking"); BankAccount account3 = new BankAccount(customer3.CustomerId, 2500, "Checking"); Console.WriteLine($"Account 1: Account # {account1.AccountNumber}, type {account1.AccountType}, balance {account1.Balance}, rate {BankAccount.InterestRate}, customer ID {account1.CustomerId}"); Console.WriteLine($"Account 2: Account # {account2.AccountNumber}, type {account2.AccountType}, balance {account2.Balance}, rate {BankAccount.InterestRate}, customer ID {account2.CustomerId}"); Console.WriteLine($"Account 3: Account # {account3.AccountNumber}, type {account3.AccountType}, balance {account3.Balance}, rate {BankAccount.InterestRate}, customer ID {account3.CustomerId}");Your console app uses top-level statements as an implicit entry point for the app (rather than using a
Mainmethod). The code in Program.cs demonstrates how to create and useBankCustomerandBankAccountobjects. the code initializes customer details, creates threeBankCustomerobjects, and prints their information. Then, it creates threeBankAccountobjects for these customers, specifying different balances and account types, and prints the account details, including account number, type, balance, interest rate, and customer ID. -
Run the app and review the output in the terminal window.
Your app should produce output that’s similar to the following example:
BankCustomer 1: Tim Shao 0014653176 BankCustomer 2: Lisa Shao 0014653177 BankCustomer 3: Sandy Zoeng 0014653178 Account 1: Account # 12885967, type Checking, balance 0, rate 0, customer ID 0014653176 Account 2: Account # 12885968, type Checking, balance 1500, rate 0, customer ID 0014653177 Account 3: Account # 12885969, type Checking, balance 2500, rate 0, customer ID 0014653178The 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.
[!TIP] To run your app, right-click the Classes_M2 project in the Solution Explorer, select Debug, and then select Start New Instance. If you don’t see the Debug option listed, ensure that you’ve selected the Classes_M2 project in the Solution Explorer. The Debug option isn’t available when the Classes_M2 solution is selected.
Implement properties for the BankCustomer class
Properties are used to encapsulate data and provide controlled access to the fields of a class. Properties use property accessors to read (get) and write (set) the values of fields. In C#, properties are defined using the get and set accessors.
In this task, you create FirstName and LastName properties for the BankCustomer class using get and set property accessors. The get and set accessors provide controlled access to the private fields FirstName and LastName. You also update the Program.cs file to use the new properties.
Use the following steps to complete this section of the exercise:
-
Open the BankCustomer.cs file.
-
Locate the FirstName and LastName fields in the
BankCustomerclass:public string FirstName = "Tim"; public string LastName = "Shao"; -
To change the fields from public to private, update the
FirstNameandLastNamefields to the following code:private string _firstName = "Tim"; private string _lastName = "Shao";The
_firstNameand_lastNamefields are now private, meaning they can only be accessed from within theBankCustomerclass. This is a common practice in object-oriented programming to encapsulate data and prevent direct access to fields from outside the class. -
To create a
FirstNameproperty that accesses the private_firstNamefield, add the following code below the field declarations in theBankCustomerclass:public string FirstName { get { return _firstName; } set { _firstName = value; } }The
FirstNameproperty is used to encapsulate the private field_firstNameand provides controlled access to it.The
publickeyword indicates that theFirstNameproperty is accessible from outside the class, meaning other classes and code can read and modify this property. The property is of typestring, which means it holds text data.The property has two accessors:
getandset. Thegetaccessor is used to retrieve the value of the private field_firstName. When the property is accessed, thegetaccessor returns the current value of_firstName.The
setaccessor is used to assign a new value to the private field_firstName. The keywordvaluerepresents the value being assigned to the property. When a new value is assigned toFirstName, thesetaccessor sets_firstNameto this new value. -
To create a
LastNameproperty that accesses the private_lastNamefield, add the following code to theBankCustomerclass:public string LastName { get { return _lastName; } set { _lastName = value; } }The
LastNameproperty works the same way as theFirstNameproperty. It encapsulates the private field_lastNameand provides controlled access to it. -
Open the Program.cs file.
-
Locate the following code statements:
Console.WriteLine($"BankCustomer 1: {customer1.FirstName} {customer1.LastName} {customer1.CustomerId}"); Console.WriteLine($"BankCustomer 2: {customer2.FirstName} {customer2.LastName} {customer2.CustomerId}"); Console.WriteLine($"BankCustomer 3: {customer3.FirstName} {customer3.LastName} {customer3.CustomerId}"); // Create accounts for customers BankAccount account1 = new BankAccount(customer1.CustomerId); BankAccount account2 = new BankAccount(customer2.CustomerId, 1500, "Checking"); BankAccount account3 = new BankAccount(customer3.CustomerId, 2500, "Checking"); -
To demonstrate the new
BankCustomerproperties, replace the code statements identified in the previous step with the following code snippet:Console.WriteLine($"BankCustomer 1: {customer1.FirstName} {customer1.LastName} {customer1.CustomerId}"); Console.WriteLine($"BankCustomer 2: {customer2.FirstName} {customer2.LastName} {customer2.CustomerId}"); Console.WriteLine($"BankCustomer 3: {customer3.FirstName} {customer3.LastName} {customer3.CustomerId}"); // Create accounts for customers BankAccount account1 = new BankAccount(customer1.CustomerId); BankAccount account2 = new BankAccount(customer2.CustomerId, 1500, "Checking"); BankAccount account3 = new BankAccount(customer3.CustomerId, 2500, "Checking"); // Demonstrate the use of BankCustomer properties customer1.FirstName = "Thomas"; customer1.LastName = "Margand"; // customer1.CustomerId = "1234567890"; // This line will not compile Console.WriteLine($"Updated BankCustomer 1: {customer1.FirstName} {customer1.LastName} {customer1.CustomerId}");Notice that your code now uses the
FirstNameandLastNameproperties of theBankCustomerclass to access the first name and last name of each customer. The code usescustomer1to demonstrate changing a customer’s first and last name, and then displays the updated customer data.You may also notice the commented code line that assigns a value to the
CustomerIdfield directly. TheCustomerIdfield is read-only and cannot be changed after it’s initialized in the constructor. If you uncomment this code line, your app won’t compile. -
Take a minute to review your code.
BankCustomer.cs
public class BankCustomer { private static int s_nextCustomerId; private string _firstName = "Tim"; private string _lastName = "Shao"; public readonly string CustomerId; public string FirstName { get { return _firstName; } set { _firstName = value; } } public string LastName { get { return _lastName; } set { _lastName = value; } } static BankCustomer() { Random random = new Random(); s_nextCustomerId = random.Next(10000000, 20000000); } public BankCustomer(string firstName, string lastName) { FirstName = firstName; LastName = lastName; this.CustomerId = (s_nextCustomerId++).ToString("D10"); } }Program.cs
string firstName = "Tim"; string lastName = "Shao"; BankCustomer customer1 = new BankCustomer(firstName, lastName); firstName = "Lisa"; BankCustomer customer2 = new BankCustomer(firstName, lastName); firstName = "Sandy"; lastName = "Zoeng"; BankCustomer customer3 = new BankCustomer(firstName, lastName); Console.WriteLine($"BankCustomer 1: {customer1.FirstName} {customer1.LastName} {customer1.CustomerId}"); Console.WriteLine($"BankCustomer 2: {customer2.FirstName} {customer2.LastName} {customer2.CustomerId}"); Console.WriteLine($"BankCustomer 3: {customer3.FirstName} {customer3.LastName} {customer3.CustomerId}"); // Create accounts for customers BankAccount account1 = new BankAccount(customer1.CustomerId); BankAccount account2 = new BankAccount(customer2.CustomerId, 1500, "Checking"); BankAccount account3 = new BankAccount(customer3.CustomerId, 2500, "Checking"); // Demonstrate the use of BankCustomer properties customer1.FirstName = "Thomas"; customer1.LastName = "Margand"; // customer1.CustomerId = "1234567890"; // This line will not compile Console.WriteLine($"Updated BankCustomer 1: {customer1.FirstName} {customer1.LastName} {customer1.CustomerId}"); Console.WriteLine($"Account 1: Account # {account1.AccountNumber}, type {account1.AccountType}, balance {account1.Balance}, rate {BankAccount.InterestRate}, customer ID {account1.CustomerId}"); Console.WriteLine($"Account 2: Account # {account2.AccountNumber}, type {account2.AccountType}, balance {account2.Balance}, rate {BankAccount.InterestRate}, customer ID {account2.CustomerId}"); Console.WriteLine($"Account 3: Account # {account3.AccountNumber}, type {account3.AccountType}, balance {account3.Balance}, rate {BankAccount.InterestRate}, customer ID {account3.CustomerId}"); -
Run the app and review the output in the terminal window.
Your app should produce output that’s similar to the following example:
BankCustomer 1: Tim Shao 0010448234 BankCustomer 2: Lisa Shao 0010448235 BankCustomer 3: Sandy Zoeng 0010448236 Updated BankCustomer 1: Thomas Margand 0010448234 Account 1: Account # 16005571, type Checking, balance 0, rate 0, customer ID 0010448234 Account 2: Account # 16005572, type Checking, balance 1500, rate 0, customer ID 0010448235 Account 3: Account # 16005573, type Checking, balance 2500, rate 0, customer ID 0010448236
Create automatically implemented properties for the BankAccount class
Automatically implemented properties provide a simplified syntax for defining properties in C#. They automatically create a private, anonymous backing field that stores the property value. This allows you to define properties without explicitly defining the private fields that store the property values.
In this task, you create automatically implemented properties for the BankAccount class.
Use the following steps to complete this section of the exercise:
-
Open the BankAccount.cs file.
-
Notice that the
BankAccountclass has two public read-write fields:BalanceandAccountType.Balanceis a read-write field that can be changed at any time.AccountTypeis a read-write field that can be changed at any time.
The other fields in the
BankAccountclass are either static or read-only:AccountNumberis a read-only field that is set in the constructor and cannot be changed after that.CustomerIdis a read-only field that is set in the constructor and cannot be changed after that.InterestRateis a static field that is shared among all instances of theBankAccountclass.s_nextAccountNumberis a static field that is shared among all instances of theBankAccountclass.
-
To convert the
BalanceandAccountTypefields to automatically implemented properties, replace theBalanceandAccountTypefield declarations with the following code:public double Balance { get; set; } = 0; public string AccountType { get; set; } = "Checking";The
BalanceandAccountTypeproperties are automatically implemented properties that encapsulate anonymous backing fields that store the property values. The anonymous backing fields (_balanceand_accountType) are created automatically by the C# compiler, so they’re not explicitly defined in the code.The
publickeyword indicates that the properties are accessible from outside the class, meaning other classes and code can read and write property values.The properties have two accessors:
getandset. Thegetaccessor is used to retrieve the value of the private field. When the property is accessed, thegetaccessor returns the current value of the private field.The
setaccessor is used to assign a new value to the private field. Thevaluekeyword represents the value being assigned to the property. When a new value is assigned to the property, thesetaccessor assignsvalueto the private field.[!NOTE] Your code doesn’t need to explicitly define the anonymous backing fields (
_balanceand_accountType) for auto-implemented properties. The C# compiler automatically creates these fields for you when using auto-implemented properties. -
Notice that the final constructor is now assigning values to the
BalanceandAccountTypeproperties.Your updated constructor uses the
balanceandaccountTypeparameters to assign values to theBalanceandAccountTypeproperties. -
Open the Program.cs file.
-
Locate the
Console.WriteLinestatements that display the account information.Console.WriteLine($"Account 1: Account # {account1.AccountNumber}, type {account1.AccountType}, balance {account1.Balance}, rate {BankAccount.InterestRate}, customer ID {account1.CustomerId}"); Console.WriteLine($"Account 2: Account # {account2.AccountNumber}, type {account2.AccountType}, balance {account2.Balance}, rate {BankAccount.InterestRate}, customer ID {account2.CustomerId}"); Console.WriteLine($"Account 3: Account # {account3.AccountNumber}, type {account3.AccountType}, balance {account3.Balance}, rate {BankAccount.InterestRate}, customer ID {account3.CustomerId}");Notice that your code uses the
BalanceandAccountTypeproperties of theBankAccountclass to access the balance and account type of each account. -
Take a minute to review your code.
BankAccount.cs
public class BankAccount { private static int s_nextAccountNumber; public readonly int AccountNumber; public static double InterestRate; public readonly string CustomerId; public double Balance { get; set; } = 0; public string AccountType { get; set; } = "Checking"; static BankAccount() { Random random = new Random(); s_nextAccountNumber = random.Next(10000000, 20000000); InterestRate = 0; } public BankAccount(string customerIdNumber) { this.AccountNumber = s_nextAccountNumber++; this.CustomerId = customerIdNumber; } public BankAccount(string customerIdNumber, double balance, string accountType) { this.AccountNumber = s_nextAccountNumber++; this.CustomerId = customerIdNumber; this.Balance = balance; this.AccountType = accountType; } }Program.cs
using Classes_M2; string firstName = "Tim"; string lastName = "Shao"; BankCustomer customer1 = new BankCustomer(firstName, lastName); firstName = "Lisa"; BankCustomer customer2 = new BankCustomer(firstName, lastName); firstName = "Sandy"; lastName = "Zoeng"; BankCustomer customer3 = new BankCustomer(firstName, lastName); Console.WriteLine($"BankCustomer 1: {customer1.FirstName} {customer1.LastName} {customer1.CustomerId}"); Console.WriteLine($"BankCustomer 2: {customer2.FirstName} {customer2.LastName} {customer2.CustomerId}"); Console.WriteLine($"BankCustomer 3: {customer3.FirstName} {customer3.LastName} {customer3.CustomerId}"); // Create accounts for customers BankAccount account1 = new BankAccount(customer1.CustomerId); BankAccount account2 = new BankAccount(customer2.CustomerId, 1500, "Checking"); BankAccount account3 = new BankAccount(customer3.CustomerId, 2500, "Checking"); // Demonstrate the use of BankCustomer properties customer1.FirstName = "Thomas"; customer1.LastName = "Margand"; // customer1.CustomerId = "1234567890"; // This line will not compile Console.WriteLine($"Updated BankCustomer 1: {customer1.FirstName} {customer1.LastName} {customer1.CustomerId}"); Console.WriteLine($"Account 1: Account # {account1.AccountNumber}, type {account1.AccountType}, balance {account1.Balance}, rate {BankAccount.InterestRate}, customer ID {account1.CustomerId}"); Console.WriteLine($"Account 2: Account # {account2.AccountNumber}, type {account2.AccountType}, balance {account2.Balance}, rate {BankAccount.InterestRate}, customer ID {account2.CustomerId}"); Console.WriteLine($"Account 3: Account # {account3.AccountNumber}, type {account3.AccountType}, balance {account3.Balance}, rate {BankAccount.InterestRate}, customer ID {account3.CustomerId}"); -
Run the app and review the output in the terminal window.
Your app should produce output that’s similar to the following example:
BankCustomer 1: Tim Shao 0014416130 BankCustomer 2: Lisa Shao 0014416131 BankCustomer 3: Sandy Zoeng 0014416132 Updated BankCustomer 1: Thomas Margand 0014416130 Account 1: Account # 13328120, type Checking, balance 0, rate 0, customer ID 0014416130 Account 2: Account # 13328121, type Checking, balance 1500, rate 0, customer ID 0014416131 Account 3: Account # 13328122, type Checking, balance 2500, rate 0, customer ID 0014416132
Create read-only properties for the BankAccount class
Read-only properties provide controlled access to the fields of a class by allowing the values of the properties to be read but not modified. Read-only properties are useful for ensuring that the state of an object remains consistent and can’t be changed after the object is constructed.
In this task, you create read-only properties in the BankAccount class.
Use the following steps to complete this section of the exercise:
-
Open the BankAccount.cs file.
-
Locate the two read-only fields:
AccountNumberandCustomerId.public readonly int AccountNumber; public readonly string CustomerId;The read-only fields
AccountNumberandCustomerIdare both initialized in the constructors. TheAccountNumberfield is initialized with a unique value, and theCustomerIdfield is initialized with the value of thecustomerIdNumberparameter. -
To convert the
AccountNumberandCustomerIdfields to read-only properties, replace theAccountNumberandCustomerIdfield declarations with the following code:public int AccountNumber { get; } public string CustomerId { get; }The
{ get; }syntax indicates that these properties are read-only, meaning they can be accessed (read) but not updated (written) after the object is constructed. This is useful for ensuring that the account number and customer ID remain constant once they are set.Read-only properties provide a level of immutability (the object’s state can’t be modified after it’s created) and help to protect the integrity of the account data.
Backing fields are not required for read-only properties because the C# compiler automatically creates them for you. The
{ get; }syntax tells the compiler to create an anonymous backing field to store the property value. -
Notice that the two instance constructors are now assigning values to the
AccountNumberandCustomerIdproperties:public BankAccount(string customerIdNumber) { this.AccountNumber = s_nextAccountNumber++; this.CustomerId = customerIdNumber; } public BankAccount(string customerIdNumber, double balance, string accountType) { this.AccountNumber = s_nextAccountNumber++; this.CustomerId = customerIdNumber; this.Balance = balance; this.AccountType = accountType; }The instance constructors use the properties to assign values to the private backing fields.
-
Open the Program.cs file.
-
Notice that the
Console.WriteLinestatements using properties to display the data for the bank account objects.Console.WriteLine($"Account 1: Account # {account1.AccountNumber}, type {account1.AccountType}, balance {account1.Balance}, rate {BankAccount.InterestRate}, customer ID {account1.CustomerId}"); Console.WriteLine($"Account 2: Account # {account2.AccountNumber}, type {account2.AccountType}, balance {account2.Balance}, rate {BankAccount.InterestRate}, customer ID {account2.CustomerId}"); Console.WriteLine($"Account 3: Account # {account3.AccountNumber}, type {account3.AccountType}, balance {account3.Balance}, rate {BankAccount.InterestRate}, customer ID {account3.CustomerId}");The
InterestRatefield is accessed using theBankAccountclass because it’s a static (and public) field. -
Run the app and review the output in the terminal window.
Your app should produce output that’s similar to the following example:
BankCustomer 1: Tim Shao 0010453163 BankCustomer 2: Lisa Shao 0010453164 BankCustomer 3: Sandy Zoeng 0010453165 Updated BankCustomer 1: Thomas Margand 0010453163 Account 1: Account # 15760425, type Checking, balance 0, rate 0, customer ID 0010453163 Account 2: Account # 15760426, type Checking, balance 1500, rate 0, customer ID 0010453164 Account 3: Account # 15760427, type Checking, balance 2500, rate 0, customer ID 0010453165
Create methods for the BankCustomer and BankAccount classes
Methods are used to define the behavior of a class. They encapsulate the logic that operates on the data stored in the class fields.
In this task, you create methods for the BankCustomer and BankAccount classes.
Use the following steps to complete this section of the exercise:
-
Open the BankCustomer.cs file.
The
BankCustomerclass has two properties:FirstNameandLastName. You can create methods that perform operations on these properties. For example, you can create a method to return the full name of the customer by combining the first and last names. You can also create methods to update the customer’s name and display customer information. -
Create a blank code line below the final constructor.
-
To create a method that returns the full name of the customer, add the following code:
// Method to return the full name of the customer public string ReturnFullName() { return $"{FirstName} {LastName}"; }The
ReturnFullNamemethod concatenates theFirstNameandLastNameproperties to return the full name of the customer. -
To create a method that updates the customer’s name, add the following code:
// Method to update the customer's name public void UpdateName(string firstName, string lastName) { FirstName = firstName; LastName = lastName; }The
UpdateNamemethod takes two parameters,firstNameandlastName, and updates theFirstNameandLastNameproperties with the new values. -
To create a method that displays customer information, add the following code:
// Method to display customer information public string DisplayCustomerInfo() { return $"Customer ID: {CustomerId}, Name: {ReturnFullName()}"; }The
DisplayCustomerInfomethod returns a string that includes the customer ID and the full name of the customer. It uses theReturnFullNamemethod to get the full name. -
Open the BankAccount.cs file.
Account balances are affected by deposits, withdrawals, and transfers. The interest rate for an account can also affect the balance. You can create methods for each of these these behaviors. Once the deposit and withdrawal methods are implemented, the
Balanceproperty can be converted from an auto-implemented property to a property with a private backing field. This ensures that the account balance can only be updated through the methods. -
Create a blank code line below the final constructor.
-
To create a method that deposits money into the account, add the following code:
// Method to deposit money into the account public void Deposit(double amount) { if (amount > 0) { Balance += amount; } }The
Depositmethod takes an amount as a parameter and adds it to theBalanceproperty if the amount is greater than zero. -
To create a method that withdraws money from the account, add the following code:
// Method to withdraw money from the account public bool Withdraw(double amount) { if (amount > 0 && Balance >= amount) { Balance -= amount; return true; } return false; }The
Withdrawmethod takes an amount as a parameter and subtracts it from theBalanceproperty if the amount is greater than zero and the balance is sufficient. The method returnstrueif the withdrawal is successful andfalseotherwise. -
To create a method that transfers money to another account, add the following code:
// Method to transfer money to another account public bool Transfer(BankAccount targetAccount, double amount) { if (Withdraw(amount)) { targetAccount.Deposit(amount); return true; } return false; }The
Transfermethod takes aBankAccountobject and anamountvariable as parameters. It uses theWithdrawmethod to withdraw theamountvalue from the current account. It uses the target account’sDepositmethod (targetAccount.Deposit) to deposit theamountvalue into the target account. The method returnstrueif the transfer is successful andfalseotherwise. -
To create a method that applies interest to the account balance, add the following code:
// Method to apply interest to the account public void ApplyInterest() { Balance += Balance * InterestRate; }The
ApplyInterestmethod calculates the interest on the account balance using theInterestRatefield and adds the interest to theBalanceproperty. At this point, theInterestRatefield is a static field that’s shared among all instances of theBankAccountclass. Since interest rate is initialized to 0, theApplyInterestmethod doesn’t actually apply any interest. You can update theInterestRatefield to a non-zero value in the static constructor to see the effect of theApplyInterestmethod. -
Take a minute to consider the current implementation of the
Balanceproperty.public double Balance { get; set; } = 0;The
Balanceproperty currently uses auto-implemented property syntax. The{ get; set; }syntax automatically creates a private backing field for the value, which is initialized to0. SinceBalanceis declaredpublic, theBalanceproperty can be modified directly from outside the class, without going through theDeposit,Withdraw, orTransfermethods. This can lead to inconsistent account balances and make it difficult to track changes to the account balance.You can convert the
Balanceproperty to a read-only property with a private backing field to prevent direct modification of the balance value from outside the class. This ensures that the balance can only be updated through theDeposit,Withdraw, andTransfermethods. -
To convert the
Balanceproperty to a read-only property with a private backing field, replace theBalanceproperty definition with the following code:public double Balance { get; private set; } = 0;The
{ get; private set; }syntax indicates that theBalanceproperty has a private setter, meaning the value of the property can only be set from within theBankAccountclass. TheBalanceproperty can still be read from outside the class, but it can only be updated through theDeposit,Withdraw, andTransfermethods. -
To create a method that displays account information, add the following code:
// Method to display account information public string DisplayAccountInfo() { return $"Account Number: {AccountNumber}, Type: {AccountType}, Balance: {Balance}, Interest Rate: {InterestRate}, Customer ID: {CustomerId}"; }The
DisplayAccountInfomethod returns a string that includes the account number, account type, balance, interest rate, and customer ID. -
Take a minute to review your updated
BankCustomerandBankAccountclasses.BankCustomer.cs
public class BankCustomer { private static int s_nextCustomerId; private string _firstName = "Tim"; private string _lastName = "Shao"; public readonly string CustomerId; public string FirstName { get { return _firstName; } set { _firstName = value; } } public string LastName { get { return _lastName; } set { _lastName = value; } } static BankCustomer() { Random random = new Random(); s_nextCustomerId = random.Next(10000000, 20000000); } public BankCustomer(string firstName, string lastName) { FirstName = firstName; LastName = lastName; this.CustomerId = (s_nextCustomerId++).ToString("D10"); } // Method to return the full name of the customer public string ReturnFullName() { return $"{FirstName} {LastName}"; } // Method to update the customer's name public void UpdateName(string firstName, string lastName) { FirstName = firstName; LastName = lastName; } // Method to display customer information public string DisplayCustomerInfo() { return $"Customer ID: {CustomerId}, Name: {ReturnFullName()}"; } }BankAccount.cs
public class BankAccount { private static int s_nextAccountNumber; public static double InterestRate; public int AccountNumber { get; } public string CustomerId { get; } public double Balance { get; private set; } = 0; public string AccountType { get; set; } = "Checking"; static BankAccount() { Random random = new Random(); s_nextAccountNumber = random.Next(10000000, 20000000); InterestRate = 0; } public BankAccount(string customerIdNumber) { this.AccountNumber = s_nextAccountNumber++; this.CustomerId = customerIdNumber; } public BankAccount(string customerIdNumber, double balance, string accountType) { this.AccountNumber = s_nextAccountNumber++; this.CustomerId = customerIdNumber; this.Balance = balance; this.AccountType = accountType; } // Method to deposit money into the account public void Deposit(double amount) { if (amount > 0) { Balance += amount; } } // Method to withdraw money from the account public bool Withdraw(double amount) { if (amount > 0 && Balance >= amount) { Balance -= amount; return true; } return false; } // Method to transfer money to another account public bool Transfer(BankAccount targetAccount, double amount) { if (Withdraw(amount)) { targetAccount.Deposit(amount); return true; } return false; } // Method to apply interest to the account public void ApplyInterest() { Balance += Balance * InterestRate; } // Method to display account information public string DisplayAccountInfo() { return $"Account Number: {AccountNumber}, Type: {AccountType}, Balance: {Balance}, Interest Rate: {InterestRate}, Customer ID: {CustomerId}"; } }
Create extension methods for the BankCustomer and BankAccount classes
Extension methods are a powerful feature in C# that allow you to add new methods to existing classes without modifying the original class. Extension methods are defined as static methods in a static class and are used as if they were instance methods of the extended class. If you have a class that you can’t modify (for example, a class from a third-party library), you can use extension methods to add new functionality to that class. If you have access to the source code of the class, adding the new methods directly to the class is recommended. However, extension methods are useful when you can’t or don’t want to modify the original class.
In this task, you create extension methods for the BankCustomer and BankAccount classes.
Use the following steps to complete this section of the exercise:
-
Use the Classes_M2 project to create a new
Extensions.csclass file.You can use the
Classes_M2project to create new class file named Extensions. Right-click the Classes_M2 project in the Solution Explorer, select New File, select Class, and then enter Extensions.Your
Extensionsclass should look similar to the following code snippet:using System; namespace Classes_M2; public class Extensions { } -
To create a class that contains the extension methods for the
BankCustomerclass, replace the contents of the Extensions.cs file with the following code:using System; namespace Classes_M2; public static class BankCustomerExtensions { // Extension method to check if the customer ID is valid public static bool IsValidCustomerId(this BankCustomer customer) { return customer.CustomerId.Length == 10; } // Extension method to greet the customer public static string GreetCustomer(this BankCustomer customer) { return $"Hello, {customer.ReturnFullName()}!"; } }The
BankCustomerExtensionsclass contains two extension methods for theBankCustomerclass:IsValidCustomerId: This method checks if the customer ID is valid by verifying that the length of the customer ID is 10.GreetCustomer: This method greets the customer by returning a string that says “Hello” followed by the customer’s full name. The extension method uses theReturnFullNamemethod of theBankCustomerclass to get the full name.
-
To create a class that contains the extension methods for the
BankAccountclass, add the following class definition to the end of theExtensionsfile:public static class BankAccountExtensions { // Extension method to check if the account is overdrawn public static bool IsOverdrawn(this BankAccount account) { return account.Balance < 0; } // Extension method to check if a specified amount can be withdrawn public static bool CanWithdraw(this BankAccount account, double amount) { return account.Balance >= amount; } }The
BankAccountExtensionsclass contains two extension methods for theBankAccountclass:IsOverdrawn: This method checks if the account is overdrawn by verifying that the balance is less than zero.CanWithdraw: This method checks if a specified amount can be withdrawn from the account by verifying that the balance is greater than or equal to the specified amount.
-
Take a minute to review the Extensions.cs file:
using System; namespace Classes_M2; public static class BankCustomerExtensions { // Extension method to check if the customer ID is valid public static bool IsValidCustomerId(this BankCustomer customer) { return customer.CustomerId.Length == 10; } // Extension method to greet the customer public static string GreetCustomer(this BankCustomer customer) { return $"Hello, {customer.ReturnFullName}!"; } } public static class BankAccountExtensions { // Extension method to check if the account is overdrawn public static bool IsOverdrawn(this BankAccount account) { return account.Balance < 0; } // Extension method to check if a specified amount can be withdrawn public static bool CanWithdraw(this BankAccount account, double amount) { return account.Balance >= amount; } }[!NOTE] Extension methods are passed an instance of the class they’re extending as the first parameter. The
thiskeyword before the parameter type indicates that the method is an extension method for that type. In this case, thethiskeyword appears before theBankCustomerandBankAccounttypes to indicate that the methods are extension methods for those classes.
Update the Program.cs file to demonstrate the updated classes, properties, and methods
In this task, you update the Program.cs file with code that demonstrates the following steps:
- Create BankCustomer objects.
- Create BankAccount objects for the instantiated customers.
- Demonstrate the use of BankCustomer properties. For example, update the customer’s name.
- Demonstrate the use of BankAccount methods. For example, deposit, withdraw, transfer, apply interest.
- Demonstrate the use of extension methods. For example, greet the customer, check if the customer ID is valid, check if the account is overdrawn, check if a specified amount can be withdrawn.
- Display customer and account information using the DisplayCustomerInfo and DisplayAccountInfo methods. These methods are defined in the BankCustomer and BankAccount classes, respectively.
Use the following steps to complete this section of the exercise:
-
Open the Program.cs file.
-
Replace the existing code with the following code:
using Classes_M2; // Step 1: Create BankCustomer objects Console.WriteLine("Creating BankCustomer objects..."); string firstName = "Tim"; string lastName = "Shao"; BankCustomer customer1 = new BankCustomer(firstName, lastName); firstName = "Lisa"; BankCustomer customer2 = new BankCustomer(firstName, lastName); firstName = "Sandy"; lastName = "Zoeng"; BankCustomer customer3 = new BankCustomer(firstName, lastName); Console.WriteLine($"BankCustomer 1: {customer1.FirstName} {customer1.LastName} {customer1.CustomerId}"); Console.WriteLine($"BankCustomer 2: {customer2.FirstName} {customer2.LastName} {customer2.CustomerId}"); Console.WriteLine($"BankCustomer 3: {customer3.FirstName} {customer3.LastName} {customer3.CustomerId}"); // Step 2: Create BankAccount objects for customers Console.WriteLine("\nCreating BankAccount objects for customers..."); BankAccount account1 = new BankAccount(customer1.CustomerId); BankAccount account2 = new BankAccount(customer2.CustomerId, 1500, "Checking"); BankAccount account3 = new BankAccount(customer3.CustomerId, 2500, "Checking"); Console.WriteLine($"Account 1: Account # {account1.AccountNumber}, type {account1.AccountType}, balance {account1.Balance}, rate {BankAccount.InterestRate}, customer ID {account1.CustomerId}"); Console.WriteLine($"Account 2: Account # {account2.AccountNumber}, type {account2.AccountType}, balance {account2.Balance}, rate {BankAccount.InterestRate}, customer ID {account2.CustomerId}"); Console.WriteLine($"Account 3: Account # {account3.AccountNumber}, type {account3.AccountType}, balance {account3.Balance}, rate {BankAccount.InterestRate}, customer ID {account3.CustomerId}"); // Step 3: Demonstrate the use of BankCustomer properties Console.WriteLine("\nUpdating BankCustomer 1's name..."); customer1.FirstName = "Thomas"; customer1.LastName = "Margand"; Console.WriteLine($"Updated BankCustomer 1: {customer1.FirstName} {customer1.LastName} {customer1.CustomerId}"); // Step 4: Demonstrate the use of BankAccount methods Console.WriteLine("\nDemonstrating BankAccount methods..."); // Deposit Console.WriteLine("Depositing 500 into Account 1..."); account1.Deposit(500); Console.WriteLine($"Account 1 after deposit: Balance = {account1.Balance}"); // Withdraw Console.WriteLine("Withdrawing 200 from Account 2..."); bool withdrawSuccess = account2.Withdraw(200); Console.WriteLine($"Account 2 after withdrawal: Balance = {account2.Balance}, Withdrawal successful: {withdrawSuccess}"); // Transfer Console.WriteLine("Transferring 300 from Account 3 to Account 1..."); bool transferSuccess = account3.Transfer(account1, 300); Console.WriteLine($"Account 3 after transfer: Balance = {account3.Balance}, Transfer successful: {transferSuccess}"); Console.WriteLine($"Account 1 after receiving transfer: Balance = {account1.Balance}"); // Apply interest Console.WriteLine("Applying interest to Account 1..."); account1.ApplyInterest(); Console.WriteLine($"Account 1 after applying interest: Balance = {account1.Balance}"); // Step 5: Demonstrate the use of extension methods Console.WriteLine("\nDemonstrating extension methods..."); Console.WriteLine(customer1.GreetCustomer()); Console.WriteLine($"Is customer1 ID valid? {customer1.IsValidCustomerId()}"); Console.WriteLine($"Can account2 withdraw 2000? {account2.CanWithdraw(2000)}"); Console.WriteLine($"Is account3 overdrawn? {account3.IsOverdrawn()}"); // Step 6: Display customer and account information Console.WriteLine("\nDisplaying customer and account information..."); Console.WriteLine(customer1.DisplayCustomerInfo()); Console.WriteLine(account1.DisplayAccountInfo()); -
Notice that steps 4-6 demonstrate the use of the BankCustomer and BankAccount methods and extension methods.
- Step 4 demonstrates the use of BankAccount methods to deposit, withdraw, transfer, and apply interest to the account balance.
- Step 5 demonstrates the use of extension methods to greet the customer, check if the customer ID is valid, check if the account is overdrawn, and check if a specified amount can be withdrawn.
- Step 6 displays customer and account information using the DisplayCustomerInfo and DisplayAccountInfo methods.
-
Run the app and review the output in the terminal window.
Your app should produce output that’s similar to the following example:
Creating BankCustomer objects... BankCustomer 1: Tim Shao 0012396421 BankCustomer 2: Lisa Shao 0012396422 BankCustomer 3: Sandy Zoeng 0012396423 Creating BankAccount objects for customers... Account 1: Account # 11657161, type Checking, balance 0, rate 0, customer ID 0012396421 Account 2: Account # 11657162, type Checking, balance 1500, rate 0, customer ID 0012396422 Account 3: Account # 11657163, type Checking, balance 2500, rate 0, customer ID 0012396423 Updating BankCustomer 1's name... Updated BankCustomer 1: Thomas Margand 0012396421 Demonstrating BankAccount methods... Depositing 500 into Account 1... Account 1 after deposit: Balance = 500 Withdrawing 200 from Account 2... Account 2 after withdrawal: Balance = 1300, Withdrawal successful: True Transferring 300 from Account 3 to Account 1... Account 3 after transfer: Balance = 2200, Transfer successful: True Account 1 after receiving transfer: Balance = 800 Applying interest to Account 1... Account 1 after applying interest: Balance = 800 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: 0012396421, Name: Thomas Margand Account Number: 11657161, Type: Checking, Balance: 800, Interest Rate: 0, Customer ID: 0012396421
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.