Lesson 14 — Protecting Data

Encapsulation

C# access modifiers and properties protect internal state. The internal modifier adds an assembly-level scope that Java does not have.

Access Modifiers

ModifierAccessible from
privateContaining class only
protectedClass and derived classes
internalSame assembly (DLL/EXE)
protected internalSame assembly or derived classes
publicEverywhere
CSHARP — Encapsulated Learner
public class Learner {
    private string _name;
    private int    _age;
    private double _mark;

    public Learner(string name, int age, double mark) {
        Name = name;   // use properties — runs validation
        Age  = age;
        Mark = mark;
    }

    public string Name {
        get => _name;
        set {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Name cannot be empty.");
            _name = value.Trim();
        }
    }

    public int Age {
        get => _age;
        set {
            if (value < 15 || value > 80)
                throw new ArgumentOutOfRangeException(nameof(Age));
            _age = value;
        }
    }

    public double Mark {
        get => _mark;
        set {
            if (value < 0 || value > 100)
                throw new ArgumentOutOfRangeException(nameof(Mark));
            _mark = value;
        }
    }

    // Internal helper — not visible outside the class:
    private string GradeLabel() => Mark switch {
        >= 80 => "Distinction", >= 60 => "Merit",
        >= 50 => "Pass", _ => "Not yet competent"
    };

    public void PrintProfile() =>
        Console.WriteLine($"{Name} | Age {Age} | {Mark:F1}% | {GradeLabel()}");
}
CSHARP — Using the class
var l = new Learner("Thandi", 21, 74.5);
l.PrintProfile();
l.Mark = 82.0;   // setter runs

try { l.Age = -5; }
catch (ArgumentOutOfRangeException ex) {
    Console.WriteLine($"Caught: {ex.ParamName}");
}

// Direct field access is impossible:
// l._mark = 150; // compile error — private

Practice Task

Your Turn

Make all fields in a BankAccount class private. Add: read-only AccountNumber (get; init;), read/write HolderName (validates non-empty), read-only Balance (get; private set;), and methods Deposit(decimal amount) and Withdraw(decimal amount) that update Balance only after validation.

Common Mistakes

  • Public fields — always prefer properties in C#.
  • Skipping validation in setters — if you validate in the constructor but use _field = value instead of calling the property setter, you bypass your own validation.
  • Same name for backing field and property — use _age for the field and Age for the property.
  • Making internal helper methods public — keep the public surface area minimal.

Professional Tip

Encapsulation is not bureaucracy — it is a contract. The class promises: 'my data is always valid'. That promise only holds if all mutation goes through the property setters.

Mini Quiz

Which modifier restricts access to the same assembly (DLL/EXE)?