Control visibility, protect your logic, and expose only what matters
In C#, access modifiers define who can see or use the parts of your code. They are essential for encapsulation, security, and maintainability, especially when working with larger projects or collaborative teams.
Letβs dive into each modifier and see how to apply them for clean, safe class design.
π§± What Are Access Modifiers?
An access modifier controls whether a class, method, or property is accessible from other parts of your code.
| Modifier | Who can access it? |
|---|---|
public | Anywhere (most open) |
private | Only inside the current class |
protected | In current class and any subclasses |
internal | Only within the same project/assembly |
protected internal | Within subclass or same assembly |
private protected | Only in derived classes in same assembly |
π¨βπ« Example: Safe Class Design
Letβs design a BankAccount class that safely manages a balance:
public class BankAccount
{
private decimal _balance; // π Internal logic protected
public string AccountHolder { get; private set; } // Readable but not settable from outside
public decimal Balance => _balance; // Read-only access
public BankAccount(string accountHolder)
{
AccountHolder = accountHolder;
_balance = 0;
}
public void Deposit(decimal amount)
{
if (amount > 0)
_balance += amount;
}
public bool Withdraw(decimal amount)
{
if (amount <= _balance)
{
_balance -= amount;
return true;
}
return false; // Not enough funds
}
}
π§ Why Use Access Modifiers?
| Goal | Use |
|---|---|
| Hide implementation details | Use private for fields/methods |
| Expose only whatβs needed | Use public for interfaces |
| Restrict inheritance usage | Use protected or internal |
| Prevent misuse | Make setters private or protected |
π§ͺ WinForms Usage Example
BankAccount acc = new BankAccount("Alice");
acc.Deposit(100);
acc.Withdraw(50);
MessageBox.Show($"Balance: {acc.Balance}"); // β
Allowed
// acc._balance = 100000; β Not allowed β it's private
// acc.AccountHolder = "Bob"; β Can't change β private set
β
This protects your logic from accidental misuse in the UI layer
β
Forces the UI to use methods (like Withdraw) rather than tampering with fields
π Choosing the Right Modifier
| Situation | Recommended Modifier |
|---|---|
| Core logic/data fields | private |
| Public-facing class APIs | public |
| Helper methods for internal use | private |
| Reusable libraries/components | internal |
| Allowing inheritance extension | protected |
π Summary
| Modifier | Used For |
|---|---|
public | Public APIs, models, form events |
private | Fields, helper methods, internal state |
protected | Inheritance hooks |
internal | Project-level visibility |
private protected | Narrowest inheritance scope |
β Best Practices
- β
Keep fields
privateβ expose access via properties or methods - β
Use
publiconly when something must be accessed externally - β
Limit subclass exposure with
protected - β Make your class APIs intuitive and safe
- β
Avoid public setters unless truly needed (use
private set)
π§ͺ Quick Challenge
π§© Create a Vehicle class with:
privatefield:speedpublicproperty:Modelpublicmethod:Accelerate()protectedmethod:Diagnostics()(to be used by subclasses)
Add a subclass Car and test visibility.
π Want to Go Further?
- Understand class-level modifiers (
public class,internal class) - Use interfaces to expose only the contract
- Learn sealed and abstract for class inheritance control
- Explore access modifiers in other files and projects
π¬ Join us on our Object-Oriented C# Training Course