1. Introduction
Imagine you're designing a modern car. Inside—hundreds of complex parts, sensitive electronics, fine-tuned settings. Obviously, nobody should just get to the engine control unit and start twisting some screws. If everyone messes with stuff they shouldn't, the car might start acting weird, and you'll have to do a checkup to figure out who broke what.
It's the same in programming. When you create a class, you want to protect its insides from random hands—so nobody accidentally messes up important data or interferes with methods the outside world shouldn't even know about. That's the whole point of encapsulation—one of the three key principles of object-oriented programming.
Encapsulation lets you hide implementation details and clearly define what's available "outside" and what should stay inside. It's like letting the user open the glove box, but locking the hood tight. The class itself decides what data to show to outside code and what to keep secret. Thanks to this, your code gets more reliable, logical, and easier to maintain.
To put it more strictly, encapsulation is a way to combine data (fields) and behavior (methods) related to them into a single structure, while limiting direct access to the object's internal components.
2. Access Modifiers: Guarding Your Turf
In C# (and other OOP languages), encapsulation is done using access modifiers. These are like "labels" that define which class members (fields, methods, properties, etc.) are available from outside, and which are only for internal use.
We've already run into them, but let's remind ourselves what we know and expand the list of modifiers a bit:
| Modifier | Available to... |
|---|---|
|
Everyone! Any code in the project (and outside it, if the project is a library) |
|
Only from inside this same class |
|
From this class and all its descendants |
|
Only within the current assembly (project) |
|
Either from the current assembly, or from a descendant |
|
Only from a descendant inside the current assembly |
In this lecture, we'll focus on the most commonly used ones: public, private, and briefly touch on protected—so we don't overload your brain.
Splitting Internal and External
Let's write a Dog class:
public class Dog
{
public string Name;
public int Age;
public void Bark()
{
Console.WriteLine($"{Name} says: Woof!");
}
}
Here, all fields and methods are declared with the public modifier. That means anyone can change the dog's name or age:
Dog rex = new Dog("Rex", 5);
rex.Name = "Sharik"; // Unexpected identity change!
rex.Age = -999; // Serious age issues
But the Name and Age fields are super important data that you don't want just anyone messing with.
This Is Not How You Do It
Leaving all fields public puts your class logic at risk: any outside interference can make the object invalid. For example, giving a dog a negative age or a name like "%$#!??".
3. Hiding Fields: Using private
Usually, class fields are made private (with the private modifier). That means you can only change their values from inside the class itself, and not from outside.
public class Dog
{
private string name;
private int age;
public void Bark()
{
Console.WriteLine($"{name} says: Woof!");
}
}
Now, trying to access the fields directly from outside:
Dog rex = new Dog("Rex", 5);
rex.name = "Sharik"; // Compile error
The compiler will instantly say: "No access!"
Why So Strict? Where's the Flexibility?
All the "flexibility" is provided by special methods or properties (more on those in the next lecture), which let you control access and change data only by certain rules.
4. Encapsulation in Practice: Example with Control
Let's say we want to make sure a dog can't have a negative age:
public class Dog
{
private string name;
private int age;
public Dog(string name, int age)
{
this.name = name;
if (age >= 0)
this.age = age;
else
this.age = 0; // Don't let them set a "weird" age
}
public void Bark()
{
Console.WriteLine($"{name} says: Woof!");
}
// Method for safely changing the age
public void SetAge(int newAge)
{
if (newAge >= 0)
age = newAge;
// You could add an else: message about invalid value
}
}
Now, nobody from outside can mess up the fields directly. To change the age, there's a special method that does the needed check.
5. Fields vs. Access Methods (Getters/Setters)
This way—making fields private and providing methods to work with them—is called data encapsulation (data encapsulation). To read a field's value, you usually make getter methods, and for writing—setter methods.
public class Dog
{
private string name;
public Dog(string name)
{
this.name = name;
}
public string GetName()
{
return name;
}
public void SetName(string newName)
{
// You can add a name validity check here
name = newName;
}
}
But! With the appearance of properties in C#, you use these methods less and less—properties make this code way cleaner and more convenient (more on that in the next lecture).
6. Access Modifiers for Methods and Classes
Fields aren't everything! Access modifiers are used for methods (class member functions) and even for the classes themselves.
Methods that are only used inside the class (like helper functions for internal logic) are usually made private.
Public methods—that's the so-called class interface (not to be confused with the interface keyword), meaning what the user of the class can actually use.
7. Common Mistakes When Working with Encapsulation
Mistake #1: declaring everything as public.
It seems like the more that's available, the easier it is to use. In reality, this opens up access to the class's internals that shouldn't be changed from outside. This kind of code gets vulnerable and unpredictable, especially in big projects.
Mistake #2: changing an access modifier breaks outside code.
When refactoring, you might accidentally change the access modifier of a method or field, and then all the other code that used them suddenly stops compiling or working right. This is especially critical in public APIs.
Mistake #3: mixing up local variables and class fields.
Sometimes devs forget that a variable declared inside a method only lives in that method. But class fields are available in all its methods. This leads to sneaky bugs, especially if variable names match.
Mistake #4: ignoring private and protected.
A lot of people are afraid to use restricted access, worried they won't be able to get to something they need later. But that's exactly what encapsulation is about—hiding all the extra stuff, and only exposing what you really need.
GO TO FULL VERSION