Using Access Modifiers for Safe Class Design in C# WinForms

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.

ModifierWho can access it?
publicAnywhere (most open)
privateOnly inside the current class
protectedIn current class and any subclasses
internalOnly within the same project/assembly
protected internalWithin subclass or same assembly
private protectedOnly 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?

GoalUse
Hide implementation detailsUse private for fields/methods
Expose only what’s neededUse public for interfaces
Restrict inheritance usageUse protected or internal
Prevent misuseMake 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

SituationRecommended Modifier
Core logic/data fieldsprivate
Public-facing class APIspublic
Helper methods for internal useprivate
Reusable libraries/componentsinternal
Allowing inheritance extensionprotected

πŸ“š Summary

ModifierUsed For
publicPublic APIs, models, form events
privateFields, helper methods, internal state
protectedInheritance hooks
internalProject-level visibility
private protectedNarrowest inheritance scope

βœ… Best Practices

  • βœ… Keep fields private β€” expose access via properties or methods
  • βœ… Use public only 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:

  • private field: speed
  • public property: Model
  • public method: Accelerate()
  • protected method: 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