Meta Description: Learn how to enforce encapsulation in C# using properties. This article explores the benefits of encapsulation, provides examples of refactoring public fields into private fields with properties, and includes exercises for various skill levels to improve your understanding of OOP in C#.
Encapsulation is one of the fundamental principles of Object-Oriented Programming (OOP). It allows us to control how our class fields are accessed and modified, which ultimately helps maintain control over our data and ensure it behaves in a predictable way. In this article, we’ll explore how to use encapsulation in C#, focusing on private fields and public properties.
The Problem with Public Fields
Let’s say we have a class named Student:
public class Student
{
public string firstName;
public int age;
}
The fields firstName and age are public, meaning anyone with access to a Student object can freely read or change those fields:
Student student = new Student();
student.firstName = "Alice";
student.age = 15;
// The values can be changed from anywhere
student.age = -5; // Oops! A negative age makes no sense.
This approach is problematic because it doesn't protect the internal state of our class. Any code can directly change the values, even if they become invalid, as in the case of the negative age.
To solve this problem, we can use encapsulation to hide the internal fields and expose them in a controlled way through properties.
Applying Encapsulation Using Properties
To properly encapsulate the data in our Student class, we make the fields private and expose them using properties:
public class Student
{
private string firstName;
private int age;
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
public int Age
{
get { return age; }
set
{
if (value > 0) // Adding validation
{
age = value;
}
}
}
}
By using properties, we can now control access to the fields. We have added validation logic to ensure age is always a positive value.
Levels of Assignment for Practice
Now, let's go over three different levels of assignments to practice encapsulation with different use cases.
Assignment Level: Easy
- Goal: Convert fields to properties and add basic validation.
-
Scenario: Create a
Bookclass with the following fields:title(string),author(string), andnumberOfPages(int). -
Requirements:
- Convert the fields to private.
- Add public properties to expose the fields.
- Ensure
numberOfPagescannot be negative.
Solution:
public class Book
{
private string title;
private string author;
private int numberOfPages;
public string Title
{
get { return title; }
set { title = value; }
}
public string Author
{
get { return author; }
set { author = value; }
}
public int NumberOfPages
{
get { return numberOfPages; }
set
{
if (value > 0)
{
numberOfPages = value;
}
}
}
}
Assignment Level: Medium
- Goal: Add read-only and write-only properties.
-
Scenario: Create a
BankAccountclass with the following fields:accountNumber(string),balance(decimal), andowner(string). -
Requirements:
- The
accountNumbershould be read-only after it is set in the constructor. - The
balanceshould only be updated through a deposit method and should never be directly set. - Add an owner property that can be read and updated.
- The
Solution:
public class BankAccount
{
private string accountNumber;
private decimal balance;
private string owner;
public BankAccount(string accountNumber, string owner)
{
this.accountNumber = accountNumber;
this.owner = owner;
}
public string AccountNumber
{
get { return accountNumber; }
}
public decimal Balance
{
get { return balance; }
}
public string Owner
{
get { return owner; }
set { owner = value; }
}
public void Deposit(decimal amount)
{
if (amount > 0)
{
balance += amount;
}
}
}
In this example:
- The
accountNumbercan be set only through the constructor and is read-only. - The
balanceis updated through aDepositmethod, ensuring better control over how it changes. - The
ownerproperty allows for both read and write access.
Assignment Level: Difficult
- Goal: Implement controlled access and use private setters.
-
Scenario: Create an
Employeeclass with the following fields:name(string),hourlyRate(decimal), andhoursWorked(int). -
Requirements:
- Add a public property for
namethat allows both getting and setting. - The
hourlyRateshould be updated only if it is positive, and should have a private setter. - The
hoursWorkedshould have a public getter but a private setter. Add a method calledAddHoursto add to thehoursWorked. - Calculate the total pay using a
CalculatePay()method, which uses thehourlyRateandhoursWorked.
- Add a public property for
Solution:
public class Employee
{
private string name;
private decimal hourlyRate;
private int hoursWorked;
public string Name
{
get { return name; }
set { name = value; }
}
public decimal HourlyRate
{
get { return hourlyRate; }
private set
{
if (value > 0)
{
hourlyRate = value;
}
}
}
public int HoursWorked
{
get { return hoursWorked; }
private set { hoursWorked = value; }
}
public Employee(string name, decimal hourlyRate)
{
this.name = name;
HourlyRate = hourlyRate; // Using the property to leverage validation
hoursWorked = 0; // Initializing hours worked to 0
}
public void AddHours(int hours)
{
if (hours > 0)
{
hoursWorked += hours;
}
}
public decimal CalculatePay()
{
return hourlyRate * hoursWorked;
}
}
In this advanced example:
- The
nameproperty allows read and write access. - The
hourlyRatehas a private setter, so it can only be modified internally, ensuring it is never set to an invalid value externally. - The
hoursWorkedcan be read publicly but is only modified via theAddHoursmethod, which ensures that only valid hours are added. - The
CalculatePay()method allows us to compute the total payment based on thehourlyRateandhoursWorked.
Summary
In this article, we explored how to apply encapsulation in C# by changing public fields to private fields and using properties to control access to the internal data. Encapsulation helps ensure that data is handled in a controlled way, preventing invalid values from being set and hiding internal details.
We also provided three assignments of varying difficulty levels to practice the concept:
- Easy: Adding basic validation to fields using properties.
- Medium: Using read-only and write-only properties to enforce controlled access.
- Difficult: Combining properties with private setters and methods to encapsulate data fully.
Properties are powerful tools that help us create robust and maintainable classes by enforcing controlled access to the data while hiding the implementation details. Encapsulation is crucial for building reliable applications, ensuring that data can only be modified in safe and predictable ways.
Top comments (0)