Encapsulation is a key principle of object-oriented programming that helps to bundle data and methods within a class and control access to them.
It hides the internal state of an object and only exposes a controlled interface to the outside.
This helps in creating secure and modular code.
In this tutorial, we’ll cover:
1. Basics of Encapsulation
Encapsulation is about restricting direct access to certain components of an object and providing controlled access through methods or properties. In C#, encapsulation is achieved by:
- Declaring fields as private to restrict direct access.
- Providing public methods or properties to control access to these fields.
Example: Encapsulation in a Class
using System; public class Person { private string name; // Private field public void SetName(string newName) { name = newName; // Public method to set name } public string GetName() { return name; // Public method to get name } } public class EncapsulationExample { public static void Main() { Person person = new Person(); person.SetName("John"); Console.WriteLine("Person's name is: " + person.GetName()); } }
In this example:
- The name field is private, so it cannot be accessed directly from outside the class.
- The SetName and GetName methods provide controlled access to the name field.
Output:
Person's name is: John
2. Access Modifiers
C# provides access modifiers to control the visibility of class members:
- private: The member is only accessible within the class it is declared.
- public: The member is accessible from any other class.
- protected: The member is accessible within the class and any derived class.
- internal: The member is accessible within the same assembly.
- protected internal: The member is accessible within the same assembly or from derived classes.
Example: Access Modifiers
using System; public class BankAccount { private decimal balance; // Private: only accessible within this class public void Deposit(decimal amount) { balance += amount; // Public method to add to balance } public decimal GetBalance() { return balance; // Public method to retrieve balance } } public class AccessModifierExample { public static void Main() { BankAccount account = new BankAccount(); account.Deposit(100.50m); Console.WriteLine("Account balance: $" + account.GetBalance()); } }
In this example:
- balance is private, and its access is controlled via the public methods Deposit and GetBalance.
Output:
Account balance: $100.5
3. Using Properties for Encapsulation
In C#, properties provide a more elegant way to implement encapsulation. They combine the functionality of getters and setters with the simplicity of accessing fields.
Example: Properties
using System; public class Car { private string model; // Private field public string Model { get { return model; } set { model = value; } } } public class PropertyExample { public static void Main() { Car myCar = new Car(); myCar.Model = "Tesla Model S"; // Setting value using property Console.WriteLine("Car model: " + myCar.Model); // Getting value using property } }
In this example:
- Model is a property that provides controlled access to the model field.
- The property encapsulates the field and gives read-write access with get and set.
Output:
Car model: Tesla Model S
4. Encapsulation with Getters and Setters
Properties in C# can also have customized get and set logic, which can add validation or other logic before assigning or retrieving values. This is useful when you need to enforce business rules.
Example: Getters and Setters with Validation
using System; public class Employee { private decimal salary; public decimal Salary { get { return salary; } set { if (value >= 30000) { salary = value; } else { Console.WriteLine("Salary must be at least $30,000."); } } } } public class GetterSetterExample { public static void Main() { Employee employee = new Employee(); employee.Salary = 25000; // Invalid salary employee.Salary = 50000; // Valid salary Console.WriteLine("Employee's salary: $" + employee.Salary); } }
Explanation:
- Salary property has validation logic in the set accessor.
- If the salary is less than $30,000, an error message is displayed and the assignment is ignored.
Output:
Salary must be at least $30,000. Employee's salary: $50000
5. Practical Examples of Encapsulation
Example 1: Bank Account with Encapsulated Balance
A bank account class can encapsulate a balance and provide methods to deposit and withdraw money, ensuring the balance does not fall below zero.
using System; public class BankAccount { private decimal balance; public decimal Balance { get { return balance; } } public void Deposit(decimal amount) { if (amount > 0) { balance += amount; Console.WriteLine("Deposited: $" + amount); } } public void Withdraw(decimal amount) { if (amount > 0 && amount <= balance) { balance -= amount; Console.WriteLine("Withdrew: $" + amount); } else { Console.WriteLine("Insufficient balance or invalid amount."); } } } public class BankAccountExample { public static void Main() { BankAccount account = new BankAccount(); account.Deposit(100); account.Withdraw(30); Console.WriteLine("Current balance: $" + account.Balance); } }
Explanation:
- The balance field is private and can only be modified through Deposit and Withdraw methods, which include validation.
- Balance property provides read-only access to the balance.
Output:
Deposited: $100 Withdrew: $30 Current balance: $70
Example 2: Student Grade with Encapsulation
A Student class can encapsulate a grade field and validate the value before assigning it.
using System; public class Student { private int grade; public int Grade { get { return grade; } set { if (value >= 0 && value <= 100) { grade = value; } else { Console.WriteLine("Invalid grade. Must be between 0 and 100."); } } } } public class StudentExample { public static void Main() { Student student = new Student(); student.Grade = 105; // Invalid grade student.Grade = 85; // Valid grade Console.WriteLine("Student's grade: " + student.Grade); } }
Explanation:
- The Grade property validates the value before assigning it to grade.
- The grade is only set if it is between 0 and 100.
Output:
Invalid grade. Must be between 0 and 100. Student's grade: 85
Example 3: Encapsulating Product Price and Stock
A Product class can encapsulate price and stock fields, with methods to add or reduce stock and set a minimum price.
using System; public class Product { private decimal price; private int stock; public decimal Price { get { return price; } set { if (value >= 0) { price = value; } else { Console.WriteLine("Price cannot be negative."); } } } public int Stock { get { return stock; } set { if (value >= 0) { stock = value; } else { Console.WriteLine("Stock cannot be negative."); } } } public void ReduceStock(int quantity) { if (quantity > 0 && quantity <= stock) { stock -= quantity; Console.WriteLine("Reduced stock by: " + quantity); } else { Console.WriteLine("Invalid quantity."); } } } public class ProductExample { public static void Main() { Product product = new Product(); product.Price = 50.75m; product.Stock = 100; product.ReduceStock(10); Console.WriteLine("Current stock: " + product.Stock); } }
Explanation:
- Price and Stock properties validate inputs before assigning values.
- The ReduceStock method ensures stock is only reduced when there’s enough stock available.
Output:
Reduced stock by: 10 Current stock: 90
Summary
Encapsulation is a crucial concept in C# that promotes security and modularity by hiding internal class details and exposing controlled access. Here’s a recap:
- Private Fields: Use private fields to restrict direct access.
- Access Modifiers: Control the visibility of class members with modifiers like private, public, protected, and internal.
- Properties: Use properties to provide controlled read/write access with validation logic if needed.
- Practical Use Cases: Encapsulation is often used in classes like BankAccount, Student, and Product to enforce constraints on internal data.
Encapsulation not only protects data but also makes classes easier to maintain and modify, providing a strong foundation for building secure and efficient applications in C#.