Read the syntax
public permits access from other code; private restricts access to the declaring type. internal limits access to the same assembly, while protected permits access within a type and its derived types.
Access modifiers describe where members may be used, not whether their values are valid. Expose controlled operations or properties instead of making mutable implementation fields public.
Syntax
public | private | protected | internalExamples
The Main Access Levels
Demonstrating the most commonly used modifiers on fields within a class.
public class AccessDemo
{
public int PublicField = 1; // accessible from anywhere
protected int ProtectedField = 2; // accessible in this class + subclasses
internal int InternalField = 3; // accessible within the same project
private int privateField = 4; // accessible only within this class
private void ShowAll()
{
// all four are accessible from inside the class itself
Console.WriteLine(PublicField + ProtectedField + InternalField + privateField);
}
}- Demonstrating the most commonly used modifiers on fields within a class.
- public permits access from other code; private restricts access to the declaring type. internal limits access to the same assembly, while protected permits access within a type and its derived types.
- The class declares four differently accessible fields. ShowAll could read all four from inside AccessDemo, but nothing calls this private method, so this snippet prints nothing.
Private Fields with Public Properties
The standard encapsulation pattern: hide data, expose controlled access through a property.
Account acc = new Account(100);
// acc._balance = -500; // Error - private, can't be accessed directly
acc.Deposit(50);
Console.WriteLine(acc.Balance); // 150
public class Account
{
private double _balance; // hidden from outside access
public Account(double initialBalance)
{
_balance = initialBalance;
}
public double Balance => _balance; // controlled read access
public void Deposit(double amount)
{
if (amount > 0)
{
_balance += amount;
}
}
}- The standard encapsulation pattern: hide data, expose controlled access through a property.
- public permits access from other code; private restricts access to the declaring type. internal limits access to the same assembly, while protected permits access within a type and its derived types.
- Account hides _balance and exposes a getter plus Deposit. The valid deposit raises 100 to 150, which Balance prints; the commented direct assignment cannot compile.
protected and Inheritance
protected members are accessible to subclasses, even outside the original assembly.
Car car = new Car();
car.Drive(); // Speed: 10
public class Vehicle
{
protected int Speed;
protected void Accelerate()
{
Speed += 10;
}
}
public class Car : Vehicle
{
public void Drive()
{
Accelerate(); // accessible because Car extends Vehicle
Console.WriteLine($"Speed: {Speed}");
}
}- protected members are accessible to subclasses, even outside the original assembly.
- public permits access from other code; private restricts access to the declaring type. internal limits access to the same assembly, while protected permits access within a type and its derived types.
- Car inherits Vehicle and can call its protected Accelerate method. Drive adds 10 to Speed and prints Speed: 10; unrelated code cannot access Speed directly.
Best practices
- Default to private for fields, exposing access only through public properties when genuinely needed (encapsulation)
- Use internal for types and members that should only be used within your own project/library, not exposed to external consumers
- Use protected specifically when subclasses need access, and public only for the intentional external API of a class
- Avoid the temptation to make everything public "just in case" - it removes the safety encapsulation is meant to provide
Runtime & versions
.NET runtime. Feature availability depends on the language and runtime version used by your project; consult the official reference below.
C# language reference