Skip to Content
Course content

162: Access Modifiers: public, private, protected, internal

Click on the "Edit" button in the top corner of the screen to edit your slide content.

In a real-world project, you'll quickly realize that making everything public is a recipe for disaster. When you're working on a large codebase with other developers, you want to control exactly who can touch which part of your logic. This is where access modifiers come in—they're essentially the "keep out" signs of your code.

Keeping the internal wiring hidden

Let's imagine we're building a system for a smart home. We'll start with a base SmartDevice class. I want the outside world to be able to turn the device on and off, but I absolutely do not want them messing with the internal voltage or the hardware status codes.

public class SmartDevice
{
    public string DeviceName { get; set; }
    private double _currentVoltage = 110.0;

    public void PowerOn()
    {
        Console.WriteLine($"{DeviceName} is now powered on.");
        // I can access _currentVoltage here because we're inside the class
        CheckVoltage();
    }

    private void CheckVoltage()
    {
        Console.WriteLine($"Voltage is stable at {_currentVoltage}V.");
    }
}

By marking _currentVoltage and CheckVoltage() as private, I've ensured that no other class can accidentally change the voltage or trigger a hardware check. It's encapsulated. If I tried to call device.CheckVoltage() from my main program, the compiler would slap my wrist immediately.

Sharing secrets with child classes

Now, here's where I usually make a mistake when I'm rushing. I'll create a SmartLight class that inherits from SmartDevice. I want the light to be able to access a unique DeviceId for registration purposes.

public class SmartDevice
{
    public string DeviceName { get; set; }
    public string DeviceId = "DEV-12345"; // Wait, I made this public...
    // ... other code
}

public class SmartLight : SmartDevice
{
    public void ConnectToWifi()
    {
        Console.WriteLine($"Connecting device {DeviceId} to network...");
    }
}

I just realized I made DeviceId public. That's a problem. While it allows SmartLight to use the ID, it also allows any random part of the application to overwrite the DeviceId while the program is running. That's a huge bug waiting to happen.

The fix is to use protected. This modifier says: "The outside world can't see this, but any class that inherits from me can."

public class SmartDevice
{
    public string DeviceName { get; set; }
    protected string DeviceId = "DEV-12345"; // Fixed!
    // ... other code
}

Now, SmartLight can still use DeviceId in its ConnectToWifi method, but if you try to access myLight.DeviceId from your Main method, it will fail. It's a much safer way to handle inheritance.

Handling assembly-wide access

Finally, there's internal. This one is a bit different because it's not about the class hierarchy, but about the project (the assembly) itself. Imagine we have a DeviceRegistry class that handles the database of all devices in the house.

public class DeviceRegistry
{
    internal void RegisterDeviceInternal(SmartDevice device)
    {
        Console.WriteLine($"Logging {device.DeviceName} to the system database.");
    }
}

By marking RegisterDeviceInternal as internal, I'm saying that any class within this same project/DLL can call this method, but if someone else references my compiled library in a separate project, they won't even see this method exists. It's perfect for "helper" logic that is critical for the library to function but shouldn't be exposed as part of the public API.

  • public: Open to everyone.
  • private: Only accessible inside the current class.
  • protected: Accessible inside the class and its children.
  • internal: Accessible anywhere within the same assembly (project).



📋 Practical Task

Exercise: Implementing a SecureDigitalWallet with Tiered Access

Create a system that simulates a digital wallet. You need to implement the following requirements using the correct access modifiers:

  • Create a base class called Wallet.
  • The OwnerName should be public.
  • The _balance should be private so it cannot be modified directly from outside the class.
  • Create a protected method called ApplyTransactionFee() that deducts a small amount from the balance.
  • Create a public method called Deposit() that increases the balance.
  • Create a derived class called CryptoWallet that inherits from Wallet.
  • In CryptoWallet, create a public method called SendCrypto() that calls the protected ApplyTransactionFee() method before deducting the amount from the balance.
  • Create an internal class called WalletAuditLog with a method that logs the wallet's activity (this represents a system-level tool).

Verify that you cannot access the _balance or ApplyTransactionFee() directly from your Main method.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.