Access modifiers and encapsulation
Decide who can use each part of your class with public, private, protected and internal, and design classes that are hard to misuse.
The ones you will use 95% of the time
| Modifier | Accessible from | Typical use |
|---|---|---|
public | ||
private | ||
protected | ||
internal |
Encapsulation in practice
public class BankAccount{ private decimal _balance; // hidden state private readonly List<string> _history = new(); public string Owner { get; } public decimal Balance => _balance; // read-only view public BankAccount(string owner) => Owner = owner; public void Deposit(decimal amount) { if (amount <= 0) throw new ArgumentException("Amount must be positive."); _balance += amount; Log($"Deposit {amount}"); } public bool TryWithdraw(decimal amount) { if (amount <= 0 || amount > _balance) return false; _balance -= amount; Log($"Withdraw {amount}"); return true; } private void Log(string entry) => _history.Add($"{DateTime.Now:HH:mm} {entry}");} var acc = new BankAccount("Ravi");acc.Deposit(1000);Console.WriteLine(acc.TryWithdraw(5000)); // False: rule enforcedConsole.WriteLine(acc.Balance);// acc._balance = 1_000_000; // error CS0122: inaccessibleFalse 1000
Deposit and TryWithdraw, which apply the rules. If the rules change later, you edit one class, not every place that uses it.
readonly fields
readonly field can be assigned only in its declaration or in a constructor. In the example, _history always refers to the same list (though the list’s contents can still change).
Guidelines
Start with everything private; make something more visible only when another class truly needs it.Never make fields public. Use properties.Expose operations ( Deposit) rather than raw setters (SetBalance).A small public surface means fewer ways to misuse the class and more freedom to change its internals.
Counter with a private count, a public read-only property Value, and public methods Increment(), Decrement() (never below 0) and Reset(). Show that Decrement at 0 keeps the value at 0.
Show the solution
public class Counter{ private int _count; public int Value => _count; public void Increment() => _count++; public void Decrement() { if (_count > 0) _count--; } public void Reset() => _count = 0;} var c = new Counter();c.Increment(); c.Increment(); c.Decrement(); c.Decrement(); c.Decrement();Console.WriteLine(c.Value);0
Remember
Encapsulation: hide state, expose a small set of safe operations. private(class only),protected(+ subclasses),internal(same project),public(all).Members are private by default; classes are internal by default. Keep fields private and change state only through validated methods.