POO : Encapsulation et polymorphisme en C# (.NET) : guide détaillé
What is this and why? (a real-life analogy)
Imagine you are a head chef in a restaurant. You have a refrigerator, a stove, knives, and ingredients. If every visitor could come into the kitchen, grab a knife, and start cutting meat, chaos would ensue. Someone would get cut, ingredients would spoil, and the dishes would turn out tasteless. To avoid this, you use encapsulation: you hide the complex processes (cutting, frying) behind a simple interface — the menu. The visitor only sees "Steak with vegetables," but how you prepare it is your internal kitchen.
Now imagine you can cook not only steak but also fish and pasta. When an order "Prepare a dish" comes in, you polymorphically choose the right recipe depending on what was ordered. For steak — one method, for fish — another, but the command is the same: "Prepare." This is polymorphism: one interface — many implementations.
In programming, these two principles help make code:
- Safe — no one will corrupt data from inside the object
- Flexible — you can add new types without breaking old code
- Understandable — complexity is hidden, only what's needed is on the outside
Important: Encapsulation and polymorphism are not just language features, but a way of thinking. A good programmer always thinks: "What am I hiding?", "How can I extend this without changes?"
Theory: how it works internally
Encapsulation: data protection
Encapsulation is the bundling of data (fields) and methods (functions) within a single object, while internal details are hidden from the outside world. In C#, this is implemented through access modifiers:
| Modifier | Access | Analogy |
|---|---|---|
public |
Open to everyone | Restaurant menu |
private |
Only within the class | Chef's recipe |
protected |
Class and its inheritors | Secrets passed to apprentices |
internal |
Within the assembly (project) | Kitchen for restaurant staff |
When you make a field private, you are saying: "No one can change this directly." To provide access to the data, you create properties or getter/setter methods. This is like a waiter who takes the order but doesn't meddle with the pots.
Polymorphism: one interface — many forms
Polymorphism (from Greek "many forms") allows objects of different classes to respond to the same calls in their own way. In C#, there are two main types:
- Inheritance polymorphism — via virtual methods (
virtual/override) - Interface polymorphism — via contracts (
interface)
When you call a method on an object, C# looks at the actual type of the object, not the type of the variable. This is called dynamic dispatch. Imagine you say "Make a sound" to a dog — it barks, to a cat — it meows. You don't know who exactly is in front of you, but you know that each one can make a sound.
Key point: Polymorphism only works through inheritance or interfaces. If two classes don't have a common ancestor, there will be no polymorphism.
Syntax in C# (.NET): all forms and variants
Encapsulation: properties and fields
In C#, there are several ways to implement encapsulation:
// 1. Auto-properties (the simplest way)
public class Person
{
public string Name { get; set; } // Automatically creates a field
public int Age { get; private set; } // Read-only from outside
}
// 2. Full properties with logic
public class BankAccount
{
private decimal _balance; // Private field
public decimal Balance
{
get { return _balance; }
private set // Setter only inside the class
{
if (value < 0)
throw new ArgumentException("Balance cannot be negative");
_balance = value;
}
}
public void Deposit(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Amount must be positive");
Balance += amount; // Use the property, not the field
}
}
// 3. Read-only properties
public class Configuration
{
public string ConnectionString { get; } // Can only be set in the constructor
public Configuration(string connectionString)
{
ConnectionString = connectionString;
}
}
// 4. Computed properties
public class Rectangle
{
public double Width { get; set; }
public double Height { get; set; }
public double Area => Width * Height; // Computed each time
}
Polymorphism: virtual methods and overriding
// Base class with a virtual method
public class Animal
{
public string Name { get; set; }
// virtual — allows overriding
public virtual void MakeSound()
{
Console.WriteLine("The animal makes a sound");
}
}
// Derived class overrides the method
public class Dog : Animal
{
// override — overrides the virtual method
public override void MakeSound()
{
Console.WriteLine("Woof-woof!");
}
}
public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Meow-meow!");
}
}
// Using polymorphism
public class Zoo
{
public void LetAllAnimalsSpeak(Animal[] animals)
{
foreach (var animal in animals)
{
animal.MakeSound(); // The correct method will be called for each
}
}
}
```