Лого на 91. НЕГ „Проф. Константин Гълъбов“

Модул 1 · Урок 14

Капсулация и свойства (properties)

Скриване на вътрешното състояние. private/public, get/set свойства, валидация. Принципът: данните се променят само през контролирани методи.

Защо капсулация

До сега правихме полетата public — всеки можеше да ги чете и да им присвоява каквото и да е. Това е удобно, но опасно:

class BankAccount
{
    public double Balance;
}

var a = new BankAccount();
a.Balance = -1_000_000;   // 🙃 нищо не ни пречи

Капсулацията крие вътрешното състояние и позволява достъп само през контролирани методи/свойства. Така можем да валидираме всяка промяна.

Модификатори на достъп

МодификаторДостъп от
publicНавсякъде
privateСамо вътре в самия клас (по подразбиране за полета)
protectedВ класа и в наследниците му
internalВ същия assembly (проект)
protected internalinternal ИЛИ protected

Ключово

Правило: полетата винаги private, достъпът — през свойства.

Свойства (properties)

Свойството изглежда като поле, но всъщност е чифт get/set методи:

class BankAccount
{
    private double balance;            // скрито поле

    public double Balance
    {
        get { return balance; }
        set
        {
            if (value < 0)
                throw new ArgumentException("Балансът не може да е отрицателен.");
            balance = value;
        }
    }
}

var a = new BankAccount();
a.Balance = 500;            // влиза през set — валидира
Console.WriteLine(a.Balance);  // вика get

Auto-properties — съкратената форма

Когато няма специална логика, C# ти спестява boilerplate-а:

class Student
{
    public string Name { get; set; }
    public double Grade { get; set; }
}

Зад кулисите компилаторът създава скрито поле и тривиалните get/set.

Само за четене (read-only) свойства

class Person
{
    public string Name { get; }
    public int Age { get; private set; }     // могат да присвояват само методите на класа

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public void Birthday() => Age++;
}

Изчислими свойства

class Rectangle
{
    public double Width { get; set; }
    public double Height { get; set; }

    public double Area => Width * Height;        // няма set — изчислява се при всяко четене
}

Валидация в setter

class Student
{
    private double grade;

    public double Grade
    {
        get => grade;
        set
        {
            if (value < 2 || value > 6)
                throw new ArgumentOutOfRangeException(
                    nameof(value), "Оценка трябва да е между 2 и 6.");
            grade = value;
        }
    }
}

Сега никой не може да присвои невалидна стойност — нито отвън, нито вътре в други методи на класа. Базата винаги остава консистентна.

Принципи за добър дизайн

  • Полетата винаги private; достъпът — през свойства.
  • Колкото по-малко публично, толкова по-добре. Лесно да добавиш достъп, трудно да отнемеш.
  • Валидация в setter-а — едно място, винаги работи.
  • Помисли: какво може да си позволи външният свят, без да повреди обекта?

Инкапсулация + неизменяемост

За още по-голяма сигурност — направи обекта неизменяем: всички свойства { get; }, конструкторът ги присвоява веднъж. След това няма как да се промени. Това е чест подход за данни-обекти (DTO-та, value objects).

Какво трябва да запомниш

  • Капсулация = крием вътрешното, излагаме само нужното.
  • Полетата private; достъпът през свойства.
  • { get; set; } = auto-property; { get; } = read-only.
  • Setter-ът е мястото за валидация.
  • Принцип: излагай минимума, валидирай всичко.