Earn More by Sharing What You Love
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!

Tim Corey
46m 57s
In this article, we will dive into C# access modifiers, which define the visibility and accessibility of types, methods, and variables in C#. Tim Corey in his video "C# Access Modifiers (beyond public and private) - what they are, how to use them, and best practices", explains various access modifiers and demonstrates their practical usage in a console application.
This article will explain what Tim covered, along with code examples to enhance your understanding. The timestamps provided allow you to follow along with the video for a more hands-on experience.
Tim Corey starts by introducing access modifiers, explaining that they determine who can see and use a resource in C#. While we are familiar with the commonly used public and private modifiers, Tim explores six different access modifiers and their use cases.
Tim sets up a simple application to demonstrate how various access modifiers work. The application consists of a console user interface and a demo library, both in .NET framework.
public class AccessDemo
{
private void PrivateDemo() { }
internal void InternalDemo() { }
public void PublicDemo() { }
}Public Class AccessDemo
Private Sub PrivateDemo()
End Sub
Friend Sub InternalDemo()
End Sub
Public Sub PublicDemo()
End Sub
End ClassThe private modifier restricts access to the method, field, or property only within the class where it is defined.
public class AccessDemo
{
private void PrivateDemo()
{
Console.WriteLine("Private method can only be accessed within this class.");
}
public void CallPrivateDemo()
{
PrivateDemo(); // Works because it's within the same class
}
}Public Class AccessDemo
Private Sub PrivateDemo()
Console.WriteLine("Private method can only be accessed within this class.")
End Sub
Public Sub CallPrivateDemo()
PrivateDemo() ' Works because it's within the same class
End Sub
End ClassExplanation: The PrivateDemo method is only accessible within the AccessDemo class. In the video, Tim demonstrates that it cannot be accessed from outside the class, even if other classes are in the same project.
Best Practice: Use private when you want to restrict access to the internal workings of your class, ensuring that it cannot be directly altered from other parts of your application.
The internal modifier allows access to the method or property only within the same assembly (the project). This is broader than the private access modifier, as it includes all classes within the same project.
public class AccessDemo
{
internal void InternalDemo()
{
Console.WriteLine("Internal method is accessible within the same assembly.");
}
}Public Class AccessDemo
Friend Sub InternalDemo()
Console.WriteLine("Internal method is accessible within the same assembly.")
End Sub
End ClassExplanation: The InternalDemo method can be accessed by any class from within the same assembly, but not from other assemblies. In the video, Tim shows that internal allows access inside the same project but denies access from outside.
Best Practice: Use internal for methods or properties that are meant to be used only within the current assembly, such as helper functions or utilities that should not be exposed to external projects.
The public modifier allows access to the method or property from any other class or assembly. This is the most permissive access level.
public class AccessDemo
{
public void PublicDemo()
{
Console.WriteLine("Public method can be accessed from any class.");
}
}Public Class AccessDemo
Public Sub PublicDemo()
Console.WriteLine("Public method can be accessed from any class.")
End Sub
End ClassExplanation: The PublicDemo method is accessible from anywhere, including other classes in the same assembly or other assemblies. Tim demonstrates that public is the most common access modifier, especially when exposing methods in libraries.
Best Practice: Use public for methods and properties that need to be accessible by other parts of the application or external projects, such as API endpoints or widely used utilities.
The protected modifier allows access to the method or property within the class where it is defined, and in any derived classes (inheritance). This modifier is useful for object-oriented programming, especially in cases of inheritance.
public class AccessDemo
{
protected void ProtectedDemo()
{
Console.WriteLine("Protected method can be accessed within the class and derived classes.");
}
}
public class DerivedClass : AccessDemo
{
public void CallProtectedDemo()
{
ProtectedDemo(); // Accessible because of inheritance
}
}Public Class AccessDemo
Protected Sub ProtectedDemo()
Console.WriteLine("Protected method can be accessed within the class and derived classes.")
End Sub
End Class
Public Class DerivedClass
Inherits AccessDemo
Public Sub CallProtectedDemo()
ProtectedDemo() ' Accessible because of inheritance
End Sub
End ClassExplanation: The ProtectedDemo method can be accessed from the AccessDemo class and any class that inherits from it. Tim explains that protected is less common but very useful when working with inheritance.
Best Practice: Use protected when you want to allow derived classes to have access to specific methods or properties, but do not want them to be accessible outside of the class hierarchy.
The private protected modifier combines the rules of private and protected. It restricts access to methods or properties within the defining class and derived classes within the same assembly. This means it offers a further protection level and tighter boundary for inheritance-based access control compared to protected.
public class AccessDemo
{
private protected void PrivateProtectedDemo()
{
Console.WriteLine("Private Protected method can be accessed within the same assembly and derived classes.");
}
}
public class DerivedClass : AccessDemo
{
public void CallPrivateProtectedDemo()
{
PrivateProtectedDemo(); // Accessible because of inheritance within the same assembly
}
}
public class UnrelatedClass
{
public void TestAccess()
{
// PrivateProtectedDemo(); // Error: Not accessible in unrelated classes
}
}Public Class AccessDemo
Private Protected Sub PrivateProtectedDemo()
Console.WriteLine("Private Protected method can be accessed within the same assembly and derived classes.")
End Sub
End Class
Public Class DerivedClass
Inherits AccessDemo
Public Sub CallPrivateProtectedDemo()
PrivateProtectedDemo() ' Accessible because of inheritance within the same assembly
End Sub
End Class
Public Class UnrelatedClass
Public Sub TestAccess()
' PrivateProtectedDemo() ' Error: Not accessible in unrelated classes
End Sub
End ClassExplanation: The PrivateProtectedDemo method is accessible in the DerivedClass because it inherits from AccessDemo and exists in the same assembly. However, it cannot be accessed in UnrelatedClass, as this class does not inherit from AccessDemo.
Best Practice: Use the private protected access modifier sparingly when you need to tightly control inheritance access within the same assembly. This is especially useful in scenarios where exposing methods or properties across assemblies might compromise encapsulation.
The protected internal modifier combines the protected and the internal access modifier levels. It allows access from the same assembly or from derived classes, even if they are in another assembly.
public class AccessDemo
{
protected internal void ProtectedInternalDemo()
{
Console.WriteLine("Protected Internal method can be accessed within the same assembly or from derived classes.");
}
}
public class DerivedClass : AccessDemo
{
public void CallProtectedInternalDemo()
{
ProtectedInternalDemo(); // Accessible due to inheritance
}
}Public Class AccessDemo
Protected Friend Sub ProtectedInternalDemo()
Console.WriteLine("Protected Internal method can be accessed within the same assembly or from derived classes.")
End Sub
End Class
Public Class DerivedClass
Inherits AccessDemo
Public Sub CallProtectedInternalDemo()
ProtectedInternalDemo() ' Accessible due to inheritance
End Sub
End ClassExplanation: The ProtectedInternalDemo method is accessible from within the same assembly, and also from any derived class, regardless of the assembly.
Best Practice: Use protected internal when you want to expose a method to both derived classes (in other assemblies) and classes in the same assembly, but not to everyone.
Tim Corey explains the importance of using access modifiers and why everything shouldn't just be public. While making everything public might seem convenient, it introduces significant risks, including data breaches, bugs, and confusion in development. Access modifiers exist to secure information, prevent unintended public access, and provide clarity in codebases.
Tim discusses why sensitive information, such as Social Security Numbers (SSNs) or credit card numbers, should not be made public. He demonstrates a "bad class" example where data exposure occurs due to public access:
Bad Example:
public class User
{
public string SSN; // Anyone can access and modify it directly
}Public Class User
Public SSN As String ' Anyone can access and modify it directly
End ClassGood Example:
public class User
{
private string ssn;
public string GetMaskedSSN()
{
return "XXX-XX-" + ssn.Substring(ssn.Length - 4);
}
public void SetSSN(string value)
{
// Add validation if needed
ssn = value;
}
}Public Class User
Private ssn As String
Public Function GetMaskedSSN() As String
Return "XXX-XX-" & ssn.Substring(ssn.Length - 4)
End Function
Public Sub SetSSN(value As String)
' Add validation if needed
ssn = value
End Sub
End ClassStarting: 35:11
Tim explains that private methods help encapsulate behaviors that should not be directly accessible. He uses the example of a DeleteUser method as part of a larger process like employee offboarding.
Bad Example:
public class UserManager
{
public void DeleteUser(int userId)
{
// Deletes the user without considering related processes
}
}Public Class UserManager
Public Sub DeleteUser(userId As Integer)
' Deletes the user without considering related processes
End Sub
End ClassGood Example:
public class UserManager
{
public void OffboardUser(int userId)
{
RevokeAccess(userId);
DeleteUser(userId); // Used privately as part of offboarding
}
private void DeleteUser(int userId)
{
// Internal logic to delete the user
}
private void RevokeAccess(int userId)
{
// Logic to revoke system access
}
}Public Class UserManager
Public Sub OffboardUser(userId As Integer)
RevokeAccess(userId)
DeleteUser(userId) ' Used privately as part of offboarding
End Sub
Private Sub DeleteUser(userId As Integer)
' Internal logic to delete the user
End Sub
Private Sub RevokeAccess(userId As Integer)
' Logic to revoke system access
End Sub
End ClassAccess modifiers prevent bugs by ensuring data is set or retrieved with proper validation. Tim illustrates this with an example involving an Age property.
Bad Example:
public class Person
{
public int Age; // Can be directly set to an invalid value
}Public Class Person
Public Age As Integer ' Can be directly set to an invalid value
End ClassGood Example:
public class Person
{
private int age;
public int Age
{
get { return age; }
set
{
if (value < 0 || value > 120)
throw new ArgumentOutOfRangeException("Age must be between 0 and 120.");
age = value;
}
}
}Public Class Person
Private _age As Integer
Public Property Age As Integer
Get
Return _age
End Get
Set(value As Integer)
If value < 0 OrElse value > 120 Then
Throw New ArgumentOutOfRangeException("Age must be between 0 and 120.")
End If
_age = value
End Set
End Property
End ClassProper use of access modifiers simplifies development by exposing only what is necessary, avoiding confusion. For instance, in an application with thousands of methods, exposing only the public ones ensures developers see only the relevant options.
Example:
public class MathLibrary
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
private void LogCalculation(string operation, int result)
{
// Logging is internal and not exposed
}
}Public Class MathLibrary
Public Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function
Public Function Subtract(a As Integer, b As Integer) As Integer
Return a - b
End Function
Private Sub LogCalculation(operation As String, result As Integer)
' Logging is internal and not exposed
End Sub
End ClassFor large-scale projects, Tim explains how proper use of access modifiers ensures only the required parts of a library are exposed, reducing the cognitive load for developers using the library.
Example:
public class MathLibrary
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
private void LogCalculation(string operation, int result)
{
// Logging is internal and not exposed
}
}Public Class MathLibrary
Public Function Add(a As Integer, b As Integer) As Integer
Return a + b
End Function
Public Function Subtract(a As Integer, b As Integer) As Integer
Return a - b
End Function
Private Sub LogCalculation(operation As String, result As Integer)
' Logging is internal and not exposed
End Sub
End ClassBy using these access modifiers correctly, you make your job and the next person's job easier.
Tim Corey provides a clear and practical guide to mastering C# access modifiers, showing how to use them effectively to create secure, maintainable, and professional applications. His detailed explanations and real-world examples make this topic accessible for developers of all levels.
For more in-depth insights and to see these concepts in action, be sure to watch Tim's full video and explore his channel for valuable content on C# and other programming topics. It's a must-visit resource for anyone serious about improving their development skills!
Do you create content for developers working with .NET, C#, Java, Python, or Node.js? Turn your expertise into extra income!
Join our newsletter, you’ll get exclusive access on article updates. We value your privacy