Access Modifiers in C#

Understand how visibility rules shape class design and encapsulation

Posted by Hüseyin Sekmenoğlu on September 23, 2022 Programming Fundamentals

In C#, access modifiers determine the visibility and accessibility of classes, methods, properties, and other members. Choosing the right modifier helps you control how your code is exposed and reused.

Here is a breakdown of each access modifier and when to use it.


🌍 public

Accessible from anywhere, both within the same assembly and from other referenced assemblies.

Used when the member needs to be completely open to other codebases.

public class Logger {
  public void Write(string message) { ... }
}

🔒 private

Accessible only within the same class or struct. If you omit a modifier, the member defaults to private.

Useful for implementation details that must not be exposed.

class Logger {
  private void FormatMessage() { ... }
}

🛡️ protected

Accessible within the same class and by derived classes.

Use this when designing for inheritance while keeping the method hidden from general usage.

class BaseClass {
  protected void Setup() { ... }
}

🏠 internal

Accessible to any code within the same assembly, but not from another assembly.

Ideal for limiting access to components of the same project or module.

internal class Helper {
  internal void Calculate() { ... }
}

🏠🛡️ protected internal

Accessible to:

  • Any code in the same assembly

  • Derived classes in any assembly

This combines both protected and internal.

protected internal void Init() { ... }

🧱 private protected

Accessible only by:

  • Derived classes that are within the same assembly

It offers the strictest combined visibility.

private protected void Configure() { ... }

📊 Summary Table

| Caller's Location                      | `public` | `protected internal` | `protected` | `internal` | `private protected` | `private` |
| -------------------------------------- | :------: | :------------------: | :---------: | :--------: | :-----------------: | :-------: |
| Same class                             |    ✔️    |          ✔️          |      ✔️     |     ✔️     |          ✔️         |     ✔️    |
| Derived class (same assembly)          |    ✔️    |          ✔️          |      ✔️     |     ✔️     |          ✔️         |     ❌     |
| Non-derived class (same assembly)      |    ✔️    |          ✔️          |      ❌      |     ✔️     |          ❌          |     ❌     |
| Derived class (different assembly)     |    ✔️    |          ✔️          |      ✔️     |      ❌     |          ❌          |     ❌     |
| Non-derived class (different assembly) |    ✔️    |           ❌          |      ❌      |      ❌     |          ❌          |     ❌     |

Final Tip:
Use access modifiers intentionally to protect internal logic, support inheritance when needed, and keep your code maintainable. Choosing the right visibility is a critical part of robust class design.

Reference: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/access-modifiers