Access Modifiers - Controlling Visibility in C#
Vaibhav • September 10, 2025
In the previous article, we explored constructors - the special methods that initialize objects and ensure they start life in a valid state. Now that we understand how to create and configure objects, it’s time to learn how to control access to their members. This is where access modifiers come in.
Access modifiers define the visibility and accessibility of classes, fields, methods, properties, and other members. They are a key part of encapsulation, one of the core principles of object-oriented programming. By using access modifiers effectively, you can protect internal implementation details, expose only what’s necessary, and design clean, maintainable APIs.
What Are Access Modifiers?
Access modifiers are keywords that specify where a member can be accessed from. In C#, the most commonly used access modifiers are:
public // Accessible from anywhere
private // Accessible only within the containing class
protected // Accessible within the containing class and derived classes
internal // Accessible within the same assembly
protected internal // Accessible within the same assembly or from derived classes
private protected // Accessible within the containing class or derived classes in the same assembly
These modifiers allow you to control the exposure of your class members and enforce boundaries between different parts of your codebase.
Using public
and private
The two most commonly used access modifiers are public
and private
. A public
member is accessible from
anywhere, while a private
member is accessible only within the class where it
is declared.
class User
{
private string password;
public string Username;
public void SetPassword(string pwd)
{
password = pwd;
}
}
In this example, Username
is public and can be accessed directly from outside
the class. password
is private and can only be accessed through the SetPassword
method. This protects sensitive data and enforces encapsulation.
Understanding protected
The protected
modifier allows access within the class and any class that
inherits from it. This is useful when designing base classes that expose functionality to derived classes but
not to external code.
class Animal
{
protected string species;
public void SetSpecies(string name)
{
species = name;
}
}
class Dog : Animal
{
public void PrintSpecies()
{
Console.WriteLine(species); // Accessible because it's protected
}
}
Here, the Dog
class inherits from Animal
and
can access the species
field because it is protected. External code cannot
access species
directly.
Using internal
for Assembly-Level Access
The internal
modifier restricts access to the current assembly. This means the
member is accessible from any class in the same project but not from other projects or assemblies.
internal class Logger
{
internal void Log(string message)
{
Console.WriteLine(message);
}
}
This is useful for hiding implementation details from external consumers while allowing full access within the same application or library.
Combining Modifiers: protected internal
The protected internal
modifier allows access from the same assembly or from
derived classes in other assemblies. It’s a combination of protected
and internal
.
class Base
{
protected internal void Display()
{
Console.WriteLine("Visible to derived classes or same assembly");
}
}
This modifier is useful when you want to expose functionality to trusted consumers (like derived classes or internal tools) but not to the general public.
Limiting Access with private protected
The private protected
modifier restricts access to the containing class and
derived classes within the same assembly. It’s more restrictive than protected internal
.
class Base
{
private protected void Configure()
{
Console.WriteLine("Accessible only in derived classes within the same assembly");
}
}
This modifier is useful when you want to allow inheritance but keep the member hidden from external assemblies.
Access Modifiers for Classes
In C#, top-level classes can be public
or internal
. You cannot declare a top-level class as private
or protected
. Nested classes (classes
declared inside another class) can use all access modifiers.
public class Outer
{
private class Inner
{
public void SayHello()
{
Console.WriteLine("Hello from Inner class");
}
}
}
In this example, Inner
is a private nested class and can only be accessed from
within Outer
.
Access Modifiers for Members
Fields, properties, methods, and constructors can use any access modifier. The default access modifier for class
members is private
if none is specified.
class Sample
{
int number; // private by default
public void Print()
{
Console.WriteLine(number);
}
}
Here, number
is private even though no modifier is specified. It can only be
accessed within the Sample
class.
Designing with Access Modifiers
Access modifiers are not just about hiding data - they are about designing clean and maintainable APIs. By exposing only what’s necessary, you reduce coupling, prevent misuse, and make your code easier to understand and evolve.
For example, you might expose a public method that performs a task, while keeping helper methods private. You might expose a read-only property to external code while allowing internal code to modify it.
class Account
{
private decimal balance;
public decimal Balance
{
get { return balance; }
}
public void Deposit(decimal amount)
{
if (amount > 0)
balance += amount;
}
}
In this example, the Balance
property is read-only, and the Deposit
method provides controlled access to modify the balance. This design
enforces rules and protects the integrity of the object.
Summary
Access modifiers are a fundamental part of object-oriented programming in C#. They allow you to control the
visibility and accessibility of classes and members, enforce encapsulation, and design clean APIs. The most
commonly used modifiers are public
, private
,
and protected
, with additional options like internal
, protected internal
, and private protected
for more nuanced control.
By using access modifiers thoughtfully, you can protect internal implementation details, expose only what’s necessary, and create code that is easier to maintain and extend. As you continue building applications in C#, access modifiers will help you enforce boundaries and write robust, well-structured code.
In the next article, we’ll explore Static Members - how to define members that belong to the class itself rather than to individual objects.