Encapsulation
Encapsulation hides internal state behind controlled interfaces — private fields, public properties with validation, and methods that enforce invariants.
Introduction
Encapsulation hides internal state behind controlled interfaces — private fields, public properties with validation, and methods that enforce invariants. It prevents callers from putting objects into invalid states, a requirement in banking balances and medical dosage models.
C# offers access modifiers (private, protected, internal, public), property accessors, and init-only setters. Interviewers link encapsulation to SOLID and ask you to refactor public fields into properties with validation.
Strong encapsulation enables refactoring internals without breaking consumers — essential for library and API design.
The story
A retail bank never exposes account balances as public fields that any caller could set to arbitrary values. Instead, customers deposit and withdraw through methods that enforce rules: deposits must be positive, withdrawals fail silently when funds are insufficient, and the balance property is read-only from outside the class.
That is encapsulation in action — the internal _balance field stays private while the public API stays safe and predictable.
Understanding the topic
Key concepts
- private hides members from outside class.
- protected visible to derived classes; internal to assembly.
- Properties wrap fields with get/set logic.
- init allows set only during initialization.
- file modifier restricts to same source file (C# 11).
- Invariant: rule always true — enforce in property setters.
Step-by-step explanation
- Consumers interact through public API surface only.
- Setters validate and normalize input before assignment.
- Getters can compute derived values without stored field.
- Backing field convention: _camelCase private field.
- Required members force explicit initialization.
- Immutability: remove public setters; use constructor/init.
Practical code example
Bank account with encapsulated balance — no direct mutation:
namespace TechLearningPro.Encapsulation;public sealed class BankAccount{private decimal _balance;public Guid Id { get; } = Guid.NewGuid();public decimal Balance => _balance;public void Deposit(decimal amount){if (amount <= 0) throw new ArgumentOutOfRangeException(nameof(amount));_balance += amount;}public bool TryWithdraw(decimal amount){if (amount <= 0 || amount > _balance) return false;_balance -= amount;return true;}}
Line-by-line code explanation
private decimal _balancehides the raw balance from external code.public Guid Id { get; } = Guid.NewGuid()exposes a read-only account identifier.public decimal Balance => _balanceis an expression-bodied getter — read-only from outside.Deposit(decimal amount)is the controlled entry point for adding money.if (amount <= 0) throw new ArgumentOutOfRangeException(...)rejects invalid deposits at the boundary._balance += amountmutates internal state only after validation passes.TryWithdraw(decimal amount)returnsfalseinstead of throwing for business-rule failures.if (amount <= 0 || amount > _balance) return falseguards both invalid and overdraft cases._balance -= amountapplies the withdrawal only when funds are sufficient.return trueconfirms the withdrawal succeeded.
Key takeaway: Balance exposed read-only via expression-bodied getter. All mutations through methods enforcing rules — classic encapsulation interview answer.
Real-world use
Where you'll use this in production
- Account balances never negative via TryWithdraw.
- Medical dosage objects rejecting out-of-range values.
- API keys stored private, exposed as masked string.
- Configuration objects with validated setters.
Best practices
- Default to private; widen access only when needed.
- Never expose mutable collections publicly — return IReadOnlyList.
- Validate in one place — property or method, not both inconsistently.
- Use init for DTOs passed across layers.
- Document invariants in XML comments for public APIs.
Common mistakes
- public List
Items allowing external Add bypassing rules. - Empty setter with no validation.
- Leaking internal types through public method returns.
- Over-exposing protected when private suffices.
Advanced interview questions
Interview Prep
Practice concise answers, then expand each card for the explanation.
1BeginnerQuestionWhat is encapsulation?+
Answer
2BeginnerQuestionprivate vs internal?+
Answer
3IntermediateQuestionWhy properties over public fields?+
Answer
4IntermediateQuestionExpose read-only collection how?+
Answer
5AdvancedQuestionRefactor anemic domain model with public setters to rich model.+
Answer
Summary
Encapsulation protects invariants via access control and validation. Properties are the primary public surface for state. init and read-only properties support immutability. Never expose mutable internals directly. Next: inheritance and code reuse.