In this article, we are going to explain different types of access modifiers in C# and what their purpose is.
For the complete navigation of this series check out: C# Back to Basics.
Access modifiers specify the accessibility of an object and all of its members in the C# project. Hence, they help enforce encapsulation by limiting the scope of members and protecting them from unintended access or modification. Moreover, all the C# types have access modifiers implemented, even if they are not stated (default access modifier is applied).
Even though this topic is more related to the object-oriented concept, we will talk about it now, thus making it easier to understand the next article about methods, that strongly relies on access modifiers.
Access Modifiers Types
C# provides four types of access modifiers: private, public, protected, internal, and two combinations: protected-internal and private-protected.
Each of these access modifiers provides a different level of accessibility and visibility, and we can use them to control the behavior of our classes and objects.
Let’s look at each access modifier in more detail.
Private Access Modifier
The private
access modifier is the most restrictive of the access modifiers. Members declared with the private access modifier are only accessible within the class in which they are declared.
When we mark a class as private
, other classes cannot inherit it.
Let’s understand this with the BankAccount
class:
public class BankAccount { private int _balance; public int GetBalance() { return _balance; } public void Deposit(int amount) { _balance += amount; } public void Withdraw(int amount) { if (_balance - amount >= 0) { _balance -= amount; } } }
Here, the _balance
field is declared with a private
access modifier, meaning that we can only access it within the BankAccount
class.
The GetBalance()
, Deposit()
, and Withdraw()
methods are public members and hence any class outside BankAccount
can also access them:
var account = new BankAccount(); account.Deposit(100); var balance = account.GetBalance(); Console.WriteLine(balance);
However, if we try to set the value of the _balance
field, we get a compiler error saying “BankAccount.balance is inaccessible due to its protection level“:
account._balance // compiler error
The private
access modifier is typically used when a member is intended for internal use only, and should not be accessible from other parts of the program. Thus, it allows us to hide the implementation details of a class and protect its members from unintended access or modification.
Public Access Modifier
The public access modifier is the most permissive of the access modifiers. Members declared with the public access modifier are accessible from anywhere in the code, including derived classes.
When we declare a class public
, any other class can inherit it.
Let’s create a class to understand this better:
public class Calculator { public int Value { get; set; } public int IncrementValue(int value) { return value + 1; } }
The Calculator
class has a Value
property and an IncrementValue()
method.
Both the Value
property and the IncrementValue()
method are declared with a public
access modifier, so we can access them from outside the Calculator
class from the Program
class:
var calculator = new Calculator(); calculator.Value = 15; var result = calculator.IncrementValue(calculator.Value); Console.WriteLine(result);
Hence, the public
access modifier makes it possible to create classes with well-defined public interfaces, that we can easily use and test from the other parts of the code.
Protected Access Modifier
The protected keyword implies that the object is accessible inside the class and in all classes that derive from that class. We will talk in more detail about inheritance in module 2 about object-oriented programming.
Let’s create a class Shape
to understand it:
public class Shape { protected int Width { get; set; } protected int Height { get; set; } public virtual int GetArea() { return Width * Height; } }
Here, we declare a Width
and a Height
property with the protected
access modifier, meaning that they are only accessible from within the Shape
class and its derived classes.
The GetArea()
method is a public
method and we mark it as virtual, so a derived class can override it.
Now, let’s create a class that inherits from Shape
:
public class Rectangle : Shape { public Rectangle(int width, int height) { Width = width; Height = height; } }
The use of the protected
access modifier in Shape
allows the derived Rectangle
class to access the Width
and Height
fields inherited from the base class:
var rectangle = new Rectangle(10, 5); var area = rectangle.GetArea(); Console.WriteLine(area);
Thus, we use the protected
access modifier in C# for creating specialized derived classes that can access members of a base class.
Internal Access Modifier
The internal keyword specifies that the object is accessible only inside its own assembly but not in other assemblies.
Members declared with the internal
access modifier are not accessible from outside the assembly (project). It also serves as the default if no explicit access modifier is specified for a class or its members.
Let’s create a class to understand this:
internal class Logger { internal string LogMessage(string message) { return $"Logged at {DateTime.Now}, Message: {message}"; } }
Here, we have a LogMessage()
method declared with the internal
access modifier. This restricts the method access to only within the same assembly:
var logger = new Logger(); var messageLog = logger.LogMessage("This is a message"); Console.WriteLine(messageLog);
Now, if we try to access the LogMessage()
method outside the current assembly, we get a compilation error.
Let’s add another project named AnotherAssembly
to our existing project, and create a class to try to access our Logger
class:
public class ExternalLogger { public void ExternalLogMessage(string message) { var logger = new AccessModifiersInCsharp.Logger(); logger.LogMessage(message); } }
Here, if we try to instantiate the Logger
class, or to access the LogMessage()
method that exists in the AccessModifiersInCsharp
assembly, we get a compilation error “inaccessible due to its protection level“.
In conclusion, the internal
access modifier makes it possible to create classes that are useful within the same assembly but are not part of the public API.
Protected Internal Access Modifier
The protected internal access modifier is a combination of protected and internal. As a result, we can access the protected internal member only in the same assembly or in a derived class in other assemblies (projects).
Let’s modify the existing Logger
class:
internal class Logger { protected internal string LogMessage(string message) { return $"Logged at {DateTime.Now}, Message: {message}"; } }
Here, we have modified the LogMessage()
method to mark it with protected internal
access modifier.
Now, let’s create a derived class DerivedLogger
:
class DerivedLogger : Logger { public string LogMessageFromDerivedClass(string message) { return $"Derived Log: {LogMessage(message)}"; } }
Here, we create a LogMessageFromDerivedClass()
method that calls the LogMessage()
method of the Logger
class.
Since DerivedLogger
is a derived class of Logger
, it can access the LogMessage()
method because it is declared with the protected internal
access modifier.
Thus, by using the protected internal
access modifier, we can expose members that are needed for customization and derivation, but not for direct use by external code.
Private Protected Access Modifier
The private protected access modifier is a combination of private and protected keywords. We can access members inside the containing class or in a class that derives from a containing class, but only in the same assembly (project). Therefore, if we try to access it from another assembly, we will get an error.
Let’s create a class to understand this better:
public class Employee { private protected int EmployeeId { get; set; } private protected string Name { get; set; } private protected double Salary { get; set; } public virtual string GetEmployeeDetails() { return $"Employee ID: {EmployeeId}, Name: {Name}, Salary: {Salary}"; } }
The Employee
class has three private protected
properties EmployeeId
, Name
, and Salary
. We can access these properties from within the same class and any derived classes in the same project.
Let’s create a Manager
class, in which we derive from Employee
:
public class Manager : Employee { public override string GetEmployeeDetails() { EmployeeId = 1; Name = "Jacob"; Salary = 90000; var employee = new Employee(); employee.EmployeeId = 2; return $"Manager Details: {base.GetEmployeeDetails()}"; } }
The class overrides the GetEmployeeDetails()
method to show the employee details. Here, we have the access to the properties of the Employee
class as Manager
is a derived class within the same assembly.
However, if we try to instantiate the Employee
class from here and access the EmployeeId
property, we get a compilation error, “Cannot access protected member ‘Employee.EmployeeId’ via a qualifier of type ‘Employee’; the qualifier must be of type ‘Manager’ (or derived from it)”. This is because we are trying to access a protected member of the base class from the derived class through the base class’s instance.
Let’s create another derived class ExternalManager
that resides in another assembly than the Employee
class:
public class ExternalManager : Employee { public override string GetEmployeeDetails() { EmployeeId = 3; Name = "David"; Salary = 80000; return $"ExternalManager Details: {base.GetEmployeeDetails()}"; } }
Here, the ExternalManager
class is derived from Employee. However, as we define it in an external assembly, we get a compilation error, “‘Employee.EmployeeId’ is inaccessible due to its protection level”. We encounter similar errors for the other properties Name
, and Salary
.
Thus, by using private protected
access modifier, we can restrict access to certain members of our base classes to only the derived classes within the same assembly, while still allowing those derived classes to inherit and extend the functionality of the base classes.
Conclusion
So, that’s it about access modifiers. In this article, we’ve covered the six access modifiers in C#: public, private, protected, internal, protected internal, and private protected. We’ve learned about what each of these access modifiers does and their usage.
By using the right access modifier, we ensure that our code is secure, easy to use, and maintain. Finally, we can create a clear and consistent structure for our code.
Our next topic is going to be about Methods in C#.