Understanding C# Access Modifiers

Access modifiers in C# control the visibility and accessibility of classes, methods, and other members. They are essential for encapsulation, a core principle of object-oriented programming.

Key Topics

Public Access Modifier

The public access modifier allows a class member to be accessible from any other code in the same assembly or another assembly that references it.

Example: Using Public Members

public class Vehicle
{
    // Public field
    public string Brand;

    // Public method
    public void DisplayBrand()
    {
        Console.WriteLine($"Brand: {Brand}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Create a new Vehicle object
        Vehicle vehicle = new Vehicle();
        
        // Access public field
        vehicle.Brand = "Toyota";
        
        // Call public method
        vehicle.DisplayBrand();
    }
}

Output:

Brand: Toyota
                    

Explanation: The Brand field and DisplayBrand() method are declared as public, so they can be accessed from outside the Vehicle class.

Private Access Modifier

The private access modifier restricts access to the containing class. Members declared as private are accessible only within the same class.

Example: Using Private Members

public class BankAccount
{
    // Private field
    private double balance;

    // Public method to deposit money
    public void Deposit(double amount)
    {
        balance += amount;
    }

    // Public method to display balance
    public void DisplayBalance()
    {
        Console.WriteLine($"Balance: ${balance}");
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Create a new BankAccount object
        BankAccount account = new BankAccount();
        
        // Deposit money using public method
        account.Deposit(1000);
        
        // Display balance using public method
        account.DisplayBalance();
        
        // Attempting to access private field directly (uncomment to see error)
        // account.balance = 500; // Error: 'BankAccount.balance' is inaccessible due to its protection level
    }
}

Output:

Balance: $1000
                    

Explanation: The balance field is declared as private, so it cannot be accessed directly from outside the BankAccount class. Public methods Deposit() and DisplayBalance() are provided to interact with the private field.

Protected Access Modifier

The protected access modifier allows access within its own class and any derived classes. It's commonly used in inheritance scenarios.

Example: Using Protected Members

public class Animal
{
    // Protected method
    protected void Eat()
    {
        Console.WriteLine("Eating...");
    }
}

public class Dog : Animal
{
    // Public method
    public void Bark()
    {
        Console.WriteLine("Barking...");
        
        // Call protected method from base class
        Eat();
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Create a new Dog object
        Dog dog = new Dog();
        
        // Call public method
        dog.Bark();
        
        // Attempting to call protected method directly (uncomment to see error)
        // dog.Eat(); // Error: 'Animal.Eat()' is inaccessible due to its protection level
    }
}

Output:

Barking...
Eating...
                    

Explanation: The Eat() method is declared as protected in the Animal class. The Dog class, which inherits from Animal, can access the Eat() method. However, an instance of Dog cannot call Eat() directly from outside the class hierarchy.

Internal Access Modifier

The internal access modifier allows access within the same assembly but not from another assembly. This is useful for large projects split across multiple assemblies.

Example: Using Internal Members

Since internal access is about assembly boundaries, this example assumes that the classes are in the same assembly.

public class Library
{
    // Internal method
    internal void DisplayMessage()
    {
        Console.WriteLine("Welcome to the library.");
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Create a new Library object
        Library library = new Library();
        
        // Call internal method
        library.DisplayMessage();
    }
}

Output (Same Assembly):

Welcome to the library.
                    

Attempting Access from Different Assembly:

Error: 'Library.DisplayMessage()' is inaccessible due to its protection level
                    

Explanation: The DisplayMessage() method is declared as internal. It can be accessed from code within the same assembly but not from another assembly.

Protected Internal Access Modifier

The protected internal access modifier allows access within the same assembly or from derived classes in other assemblies.

Example: Using Protected Internal Members

This modifier combines the functionality of both protected and internal access modifiers.

public class BaseClass
{
    // Protected internal method
    protected internal void DisplayInfo()
    {
        Console.WriteLine("BaseClass information.");
    }
}

public class DerivedClass : BaseClass
{
    public void ShowInfo()
    {
        // Accessing protected internal method
        DisplayInfo();
    }
}

class Program
{
    static void Main(string[] args)
    {
        // Create an instance of DerivedClass
        DerivedClass derived = new DerivedClass();
        
        // Call public method that accesses protected internal member
        derived.ShowInfo();
    }
}

Output (Same Assembly or Derived Class):

BaseClass information.
                    

Attempting Direct Access from Non-Derived Class in Different Assembly:

Error: 'BaseClass.DisplayInfo()' is inaccessible due to its protection level
                    

Explanation: The DisplayInfo() method is accessible within the same assembly and by derived classes, even if they are in different assemblies. However, it cannot be accessed directly by non-derived classes in other assemblies.

Key Takeaways

  • public: Accessible from anywhere.
  • private: Accessible only within the containing class.
  • protected: Accessible within the class and its derived classes.
  • internal: Accessible within the same assembly.
  • protected internal: Accessible within the same assembly or from derived classes in other assemblies.