A Beginner's Guide to C# Access Modifiers: Learn the Basics

A Beginner's Guide to C# Access Modifiers: Learn the Basics

Access modifiers determine the accessibility of a type or type member, indicating which parts of a C# program can access those types and their members. By protecting types and members from unintended access, access modifiers contribute to achieving encapsulation.

Below is the list of all the access modifiers used in the C# language

Public

  • type or members with private access modifiers could be accessed by any code in the same assembly or another assembly referencing it.

  • public is the default access modifier for members of an enum or interface.

  • accessibility of public members is controlled by the access modifier of the type itself.

    Code example

    
      using System;
      namespace AccessModifierCode {
      class Employee {
          public int EmpId;
          public string Name;
          public Employee(int empid, string name) {
              EmpId = empid;
              Name = name;
          }
      }
      class Program {
          static void Main(string[] args) {
              // Creating object of the class Employee
              Employee E = new Employee(1, "Deepak");
              Console.WriteLine("Emp Id: {0}", E.EmpId);
              Console.WriteLine("Name: {0}", E.Name);
              Console.WriteLine();
          }
       }
      }
    

Private

  • members declared with private access modifier are accessible only within the containing type.

  • private access modifier is the most restrictive one.

  • one can not declare a private class or struct directly inside a namespace.

  • private is the default accessibility for members of a class or struct.

      public class Demo {
         string Name; //  private
      }
    
      using System;
      namespace AccessModifierCode {
      class Employee {
          public int EmpId;
          public string Name;
          private decimal Salary
          public Employee(int empid, string name, decimal salary) {
              EmpId = empid;
              Name = name;
              Salary = salary;
          }
      }
      class Program {
          static void Main(string[] args) {
              // Creating object of the class Employee
              Employee E = new Employee(1, "Deepak", 0);
              Console.WriteLine("Emp Id: {0}", E.EmpId);
              Console.WriteLine("Name: {0}", E.Name);
              Console.WriteLine("Salary: {0}", E.Salary); 
              // Error : 'Employee.Salary is inaccessible due to its protection level'
              Console.WriteLine();
          }
       }
      }
    

Internal

  • types or members declared with internal modifiers are accessible within the same assembly or friend assembly. Learn more about Friend assembly

  • internal is the default access modifier for class and struct types.

Protected

protected type or members are accessible within the same class or derived class.

public class Shape {
    protected int Width { get; set; }
    protected int Height { get; set; }
}
public class Rectangle : Shape {
    public Rectangle(int width, int height) {
        Width = width;
        Height = height;
    }
}

protected internal

  • Types or members declared with the protected internal modifier can be accessed only within the same assembly or in a derived class from another assembly.

private protected

  • A member that is private protected is accessible only within the containing type, or subclasses that reside in the same assembly (making it less accessible than protected or internal alone).

Restrictions on Access Modifiers

  • Interfaces declared directly within a namespace can be public or internal and, just like classes and structs, interfaces default to internal access.

  • Interface members are public by default.

  • Enumeration members are always public, and no access modifiers can be applied.

  • Delegates function similarly to classes and structs. When declared directly within a namespace, they have internal access by default, and when nested, they have private access.

  • Derived classes and derived records can't have greater accessibility than their base types. You can't declare a public class B that derives from an internal class A. If allowed, it would have the effect of making A public, because all protected or internal members of A are accessible from the derived class.

When overriding a base class function, accessibility must be identical to the over‐ ridden function.

class BaseClass { 
    protected virtual void Foo() {} 
}
class Subclass1 : BaseClass { 
    protected override void Foo() {} // OK
} 
class Subclass2 : BaseClass { 
    public override void Foo() {} // Error
}

Summary table

Caller's locationpublicprotected internalprotectedinternalprivate protectedprivate
Within the class✔️️✔️✔️✔️✔️✔️
Derived class (same assembly)✔️✔️✔️✔️✔️
Non-derived class (same assembly)✔️✔️✔️
Derived class (different assembly)✔️✔️✔️
Non-derived class (different assembly)✔️

(note: copied from MS learn)

So, that concludes our discussion on access modifiers. In this article, we covered the six access modifiers in C#: public, private, protected, internal, protected internal, and private protected. We learned about the purpose of each access modifier and their applications.

By employing the appropriate access modifier, we can ensure that our code is secure, user-friendly, and easy to maintain. Ultimately, this allows us to establish a clear and consistent structure for our code.

References and further reading

Did you find this article valuable?

Support Deepak Kumar Jain by becoming a sponsor. Any amount is appreciated!