What Are the Types of Access Modifiers in C#?
Often, as junior C# developers, we write code all day that works, but we see words like private
, public
, internal
, or protected
on our classes, structs, and records. These are called access modifiers — but do we really know what they mean and when to use them?
Hopefully, this blog post will help you understand C# access modifiers better.
1. Public
Public members can be accessed by any type from any assembly.
Code Example – Same Assembly:
namespace AccessModifiers;
public class TestClass
{
public string PublicField = "public";
}
public class Program
{
static void Main(string[] args)
{
var testClassObject = new TestClass();
Console.WriteLine($"Accessing public: {testClassObject.PublicField}");
Console.ReadKey();
}
}
Code Example – Another Assembly:
using AccessModifiers;
namespace AccessModifiersAnotherAssembly;
public class Program
{
static void Main(string[] args)
{
var testClassObject = new TestClass();
Console.WriteLine($"Accessing public: {testClassObject.PublicField}");
Console.ReadKey();
}
}
2. Internal
Internal members can be accessed by any other type within the same assembly in which they are defined. They cannot be accessed from outside that assembly.
Code Example:
namespace AccessModifiers;
public class TestClass
{
public string PublicField = "public";
internal string InternalField = "internal";
protected string ProtectedField = "protected";
}
public class Program
{
static void Main(string[] args)
{
var testClassObject = new TestClass();
Console.WriteLine($"Accessing public: {testClassObject.PublicField}");
Console.WriteLine($"Accessing internal: {testClassObject.InternalField}");
Console.ReadKey();
}
}
Code snippet – Another Assembly (unable to access the internal field):
3. Protected
Protected members can only be used by types that derive from the base class — in this case, TestClass
.
They can also be accessed from another assembly if the derived class exists in that assembly.
Code Example:
using AccessModifiers;
namespace AccessModifiersAnotherAssembly;
public class Program
{
static void Main(string[] args)
{
var testClassObject = new TestClass();
Console.WriteLine($"Accessing public: {testClassObject.PublicField}");
// Console.WriteLine($"Accessing protected: {testClassObject.ProtectedField}"); // Not allowed
Console.ReadKey();
}
}
public class ChildOfTestClassAnotherAssembly : TestClass
{
public ChildOfTestClassAnotherAssembly()
{
Console.WriteLine(base.ProtectedField); // Allowed - derived class
}
}
4. Private
The private
modifier restricts access to members within the same class only. It cannot be accessed from any other class.
Code Example:
public class TestClass
{
public string PublicField = "public";
internal string InternalField = "internal";
protected string ProtectedField = "protected";
protected internal string ProtectedInternalField = "protected internal";
private protected string PrivateProtectedField = "private protected";
private string PrivateField = "private";
}
5. Protected Internal
Within the same assembly, protected internal
behaves like internal
, meaning it can be accessed from any class in that assembly.
In another assembly, it behaves like protected
, meaning it’s only accessible from a derived class.
Code Example – Assembly 1:
namespace AccessModifiers;
public class TestClass
{
public string PublicField = "public";
internal string InternalField = "internal";
protected string ProtectedField = "protected";
protected internal string ProtectedInternalField = "protected internal";
}
public class ChildOfTestClass : TestClass
{
public ChildOfTestClass()
{
Console.WriteLine(base.ProtectedField);
Console.WriteLine(base.ProtectedInternalField);
}
}
public class Program
{
static void Main(string[] args)
{
var testClassObject = new TestClass();
var testClassObject2 = new ChildOfTestClass();
Console.WriteLine($"Accessing public: {testClassObject.PublicField}");
Console.WriteLine($"Accessing internal: {testClassObject.InternalField}");
Console.WriteLine($"Accessing protected internal: {testClassObject.ProtectedInternalField}");
Console.ReadKey();
}
}
Code Example – Assembly 2:
using AccessModifiers;
namespace AccessModifiersAnotherAssembly;
public class Program
{
static void Main(string[] args)
{
var testClassObject = new TestClass();
Console.WriteLine($"Accessing public: {testClassObject.PublicField}");
Console.ReadKey();
}
}
public class ChildOfTestClassAnotherAssembly : TestClass
{
public ChildOfTestClassAnotherAssembly()
{
Console.WriteLine(base.ProtectedField);
Console.WriteLine(base.ProtectedInternalField); // Works only because of inheritance
}
}
6. Private Protected
Within the same assembly, private protected
behaves like protected
— meaning it can be accessed from derived classes within the same assembly only.
Outside of the assembly, it is not accessible at all.
Code Example:
namespace AccessModifiers;
public class TestClass
{
public string PublicField = "public";
internal string InternalField = "internal";
protected string ProtectedField = "protected";
protected internal string ProtectedInternalField = "protected internal";
private protected string PrivateProtectedField = "private protected";
}
public class ChildOfTestClass : TestClass
{
public ChildOfTestClass()
{
Console.WriteLine(base.ProtectedField);
Console.WriteLine(base.ProtectedInternalField);
Console.WriteLine(base.PrivateProtectedField); // Works - same assembly
}
}
public class Program
{
static void Main(string[] args)
{
var testClassObject = new TestClass();
var testClassObject2 = new ChildOfTestClass();
Console.WriteLine($"Accessing public: {testClassObject.PublicField}");
Console.WriteLine($"Accessing internal: {testClassObject.InternalField}");
Console.WriteLine($"Accessing protected internal: {testClassObject.ProtectedInternalField}");
// Console.WriteLine(testClassObject.PrivateProtectedField); // Not accessible
Console.ReadKey();
}
}
All the examples above use classes, but access modifiers work the same way for classes, structs, and records.
However, not all access modifiers are supported by structs, since structs do not support inheritance.
Top comments (0)