10 minlesson

Access Modifiers

Access Modifiers

Access modifiers control the visibility of classes, fields, properties, and methods. They're essential for encapsulation - hiding internal details while exposing a clean public interface.

The Access Modifiers

ModifierDescription
publicAccessible from anywhere
privateAccessible only within the same class
protectedAccessible within the class and derived classes
internalAccessible within the same assembly (project)
private protectedAccessible within the class and derived classes in same assembly

public

Public members are accessible from anywhere:

csharp
1class Person
2{
3 public string Name; // Accessible from anywhere
4
5 public void SayHello()
6 {
7 Console.WriteLine($"Hello, I'm {Name}");
8 }
9}
10
11Person p = new Person();
12p.Name = "Alice"; // OK - Name is public
13p.SayHello(); // OK - SayHello is public

private

Private members are only accessible within the class:

csharp
1class BankAccount
2{
3 private decimal _balance; // Only accessible inside this class
4 private string _pin;
5
6 public void Deposit(decimal amount)
7 {
8 if (amount > 0)
9 {
10 _balance += amount; // OK - inside the class
11 }
12 }
13
14 public decimal GetBalance()
15 {
16 return _balance; // OK - inside the class
17 }
18}
19
20BankAccount account = new BankAccount();
21account.Deposit(100); // OK - Deposit is public
22decimal bal = account.GetBalance(); // OK
23// account._balance = 1000; // Error! _balance is private

Default Access

If no modifier is specified:

  • Class members default to private
  • Classes default to internal
csharp
1class Example
2{
3 int value; // private (no modifier)
4 void DoSomething() // private (no modifier)
5 {
6 }
7
8 public int PublicValue; // explicitly public
9}

Encapsulation in Practice

Hide internal details, expose controlled access:

csharp
1class Temperature
2{
3 private double _celsius; // Hidden implementation
4
5 // Public interface
6 public double Celsius
7 {
8 get => _celsius;
9 set
10 {
11 if (value < -273.15)
12 throw new ArgumentException("Below absolute zero!");
13 _celsius = value;
14 }
15 }
16
17 public double Fahrenheit
18 {
19 get => _celsius * 9 / 5 + 32;
20 set => Celsius = (value - 32) * 5 / 9;
21 }
22
23 public double Kelvin
24 {
25 get => _celsius + 273.15;
26 set => Celsius = value - 273.15;
27 }
28}
29
30Temperature temp = new Temperature();
31temp.Celsius = 25;
32Console.WriteLine($"F: {temp.Fahrenheit}"); // F: 77
33Console.WriteLine($"K: {temp.Kelvin}"); // K: 298.15

Protected Members

Accessible in the class and its derived classes (inheritance):

csharp
1class Animal
2{
3 protected string species;
4
5 public void SetSpecies(string s)
6 {
7 species = s;
8 }
9}
10
11class Dog : Animal
12{
13 public void Bark()
14 {
15 Console.WriteLine($"{species} says: Woof!"); // OK - protected
16 }
17}
18
19Dog dog = new Dog();
20dog.SetSpecies("Canine");
21dog.Bark(); // Canine says: Woof!
22// dog.species = "X"; // Error! protected not accessible from outside

Internal Members

Accessible within the same assembly (project):

csharp
1// In the same project
2internal class InternalHelper
3{
4 internal void DoWork()
5 {
6 Console.WriteLine("Working...");
7 }
8}
9
10// In Program.cs (same project)
11InternalHelper helper = new InternalHelper(); // OK
12helper.DoWork(); // OK
13
14// From another project - not accessible

Best Practices

1. Start Private, Expose as Needed

csharp
1class User
2{
3 // Private by default
4 private int _id;
5 private string _passwordHash;
6 private DateTime _lastLogin;
7
8 // Public interface
9 public int Id => _id;
10 public string Username { get; set; }
11 public bool IsActive { get; set; }
12}

2. Hide Implementation Details

csharp
1class EmailService
2{
3 // Private helper methods
4 private bool ValidateEmail(string email)
5 {
6 return email.Contains("@");
7 }
8
9 private string FormatMessage(string message)
10 {
11 return message.Trim();
12 }
13
14 // Public interface - users only see this
15 public void SendEmail(string to, string message)
16 {
17 if (!ValidateEmail(to))
18 throw new ArgumentException("Invalid email");
19
20 string formatted = FormatMessage(message);
21 // Send the email...
22 Console.WriteLine($"Sent to {to}: {formatted}");
23 }
24}

3. Protect Invariants

csharp
1class ShoppingCart
2{
3 private List<string> _items = new List<string>();
4 private bool _isCheckedOut = false;
5
6 public void AddItem(string item)
7 {
8 if (_isCheckedOut)
9 throw new InvalidOperationException("Cart is checked out");
10 _items.Add(item);
11 }
12
13 public void Checkout()
14 {
15 if (_items.Count == 0)
16 throw new InvalidOperationException("Cart is empty");
17 _isCheckedOut = true;
18 Console.WriteLine("Order placed!");
19 }
20
21 // Read-only access to items
22 public IReadOnlyList<string> Items => _items.AsReadOnly();
23}

Complete Example

csharp
1class PlayerCharacter
2{
3 // Private fields - internal state
4 private int _health;
5 private int _experience;
6 private readonly int _maxHealth;
7
8 // Public properties - controlled access
9 public string Name { get; }
10 public int Level { get; private set; }
11
12 public int Health
13 {
14 get => _health;
15 private set => _health = Math.Clamp(value, 0, _maxHealth);
16 }
17
18 public int MaxHealth => _maxHealth;
19
20 public bool IsAlive => _health > 0;
21
22 // Constructor
23 public PlayerCharacter(string name, int maxHealth = 100)
24 {
25 Name = name;
26 _maxHealth = maxHealth;
27 _health = maxHealth;
28 Level = 1;
29 _experience = 0;
30 }
31
32 // Public methods - the interface
33 public void TakeDamage(int amount)
34 {
35 if (amount < 0) return;
36 Health -= amount;
37 OnHealthChanged();
38 }
39
40 public void Heal(int amount)
41 {
42 if (amount < 0) return;
43 Health += amount;
44 OnHealthChanged();
45 }
46
47 public void GainExperience(int xp)
48 {
49 if (xp < 0) return;
50 _experience += xp;
51 CheckLevelUp();
52 }
53
54 // Private helper methods
55 private void OnHealthChanged()
56 {
57 Console.WriteLine($"{Name}: HP {_health}/{_maxHealth}");
58 if (!IsAlive)
59 {
60 Console.WriteLine($"{Name} was defeated!");
61 }
62 }
63
64 private void CheckLevelUp()
65 {
66 int xpNeeded = Level * 100;
67 while (_experience >= xpNeeded)
68 {
69 _experience -= xpNeeded;
70 Level++;
71 _health = _maxHealth; // Restore health on level up
72 Console.WriteLine($"{Name} leveled up to {Level}!");
73 xpNeeded = Level * 100;
74 }
75 }
76
77 public void DisplayStats()
78 {
79 Console.WriteLine($"=== {Name} ===");
80 Console.WriteLine($"Level: {Level}");
81 Console.WriteLine($"Health: {_health}/{_maxHealth}");
82 Console.WriteLine($"XP: {_experience}/{Level * 100}");
83 }
84}
85
86// Usage
87PlayerCharacter hero = new PlayerCharacter("Hero", 100);
88hero.TakeDamage(30);
89hero.Heal(10);
90hero.GainExperience(250); // Level up!
91hero.DisplayStats();

Summary

In this lesson, you learned:

  • public: Accessible from anywhere
  • private: Accessible only within the class
  • protected: Accessible in class and derived classes
  • internal: Accessible within the same project
  • Default access is private for members, internal for classes
  • Encapsulation hides implementation and protects data integrity
  • Start private, expose only what's necessary

Now let's practice with hands-on workshops!