Mastering C# Fundamentals: Encapsulation
mohamed Tayel
Posted on October 2, 2024
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
Book
class with the following fields:title
(string),author
(string), andnumberOfPages
(int). -
Requirements:
- Convert the fields to private.
- Add public properties to expose the fields.
- Ensure
numberOfPages
cannot 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
BankAccount
class with the following fields:accountNumber
(string),balance
(decimal), andowner
(string). -
Requirements:
- The
accountNumber
should be read-only after it is set in the constructor. - The
balance
should 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
accountNumber
can be set only through the constructor and is read-only. - The
balance
is updated through aDeposit
method, ensuring better control over how it changes. - The
owner
property allows for both read and write access.
Assignment Level: Difficult
- Goal: Implement controlled access and use private setters.
-
Scenario: Create an
Employee
class with the following fields:name
(string),hourlyRate
(decimal), andhoursWorked
(int). -
Requirements:
- Add a public property for
name
that allows both getting and setting. - The
hourlyRate
should be updated only if it is positive, and should have a private setter. - The
hoursWorked
should have a public getter but a private setter. Add a method calledAddHours
to add to thehoursWorked
. - Calculate the total pay using a
CalculatePay()
method, which uses thehourlyRate
andhoursWorked
.
- 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
name
property allows read and write access. - The
hourlyRate
has a private setter, so it can only be modified internally, ensuring it is never set to an invalid value externally. - The
hoursWorked
can be read publicly but is only modified via theAddHours
method, which ensures that only valid hours are added. - The
CalculatePay()
method allows us to compute the total payment based on thehourlyRate
andhoursWorked
.
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.
Posted on October 2, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.