JavaRush /Курси /C# SELF /Автоматичні властивості та init-only сетери

Автоматичні властивості та init-only сетери

C# SELF
Рівень 17 , Лекція 2
Відкрита

1. Автоматичні властивості

У C# властивості (property) — це щось середнє між полем і методом. По суті, це синтаксичний цукор, який робить роботу з інкапсуляцією максимально зручною. Ви звертаєтеся до властивості так, ніби це звичайне поле, а всередині може бути будь-яка логіка: перевірка даних, зміна інших полів, виклик методів і навіть надсилання електронних листів вашій бабусі (останнє — не рекомендується). У минулій лекції ми писали властивості вручну. Але це швидко набридає, якщо у вас багато простих об’єктів, де потрібні лише «ґетер» і «сетер» — без додаткової логіки.

Знайомтеся — автоматичні властивості. Вони позбавлять вас шаблонного коду і дозволять C# дбати про зберігання значення без вашої участі.

Як було раніше: ручні властивості

Коли ми хочемо сховати поле, але дати до нього доступ через властивість, зазвичай пишемо ось так:


public class Dog
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}
Ручна властивість із приватним полем

Такий код — типовий шаблонний (boilerplate). Часто в ці get/set нічого додавати не потрібно: достатньо надати доступ — і все.

Як можна тепер

У C# усе стало простіше: тепер не потрібно вручну прописувати поля — компілятор бере це на себе. Достатньо написати:


public class Dog
{
    public string Name { get; set; }
}
Автоматична властивість

Готово. Можна спокійно читати й змінювати значення Name, а за лаштунками компілятор сам створює приватне поле, до якого не дістатися напряму.

Що ж насправді відбувається за лаштунками? Коли в коді з’являється public string Name { get; set; }, компілятор автоматично створює приховане поле типу Name__BackingField. При кожному зверненні до Name він підставляє потрібний ґетер або сетер. Саме поле безпосередньо в коді не видно, але воно існує — і за допомогою рефлексії його навіть можна знайти (але це вже зовсім інша історія).

У чому перевага такого підходу? По-перше, це значно коротше і не потрібно повторювати одне й те саме. По-друге, код стає чистішим: менше візуального шуму, простіше читати й супроводжувати. І нарешті, це безпечніше — ніхто не зможе безпосередньо втручатися у поля: доступ здійснюється лише через властивості.

2. Синтаксис автоматичних властивостей

Синтаксис дуже простий:


public class Dog
{
    public string Name { get; set; }
    public int Age { get; set; }
}
Кілька автоматичних властивостей

Тепер наш Dog виглядає набагато лаконічніше! Ми можемо створювати об’єкти та задавати значення у звичний спосіб:

Dog myDog = new Dog();
myDog.Name = "Барбос";
myDog.Age = 4;

Схематично

Спосіб Синтаксис Доступ до значення
Поле
public string Name;
Доступний безпосередньо
Ручна властивість
public string Name { get; set; } + private string name;
Доступ через get/set
Автоматична властивість
public string Name { get; set; }
get/set + приховане поле

3. Налаштування доступу: лише читання, лише запис

Лише для читання: get-only

Якщо властивість має бути лише для читання (наприклад, реєстраційний номер улюбленця), можна зробити її доступною тільки для читання — вказавши get, а set або взагалі не писати, або зробити приватним. Значення можна присвоїти лише в конструкторі чи безпосередньо під час оголошення:


public class Dog
{
    public string RegistrationCode { get; }

    public Dog()
    {
        RegistrationCode = "DOG-001"; // Присвоюємо значення в конструкторі
    }
}

Тепер ззовні можна лише прочитати RegistrationCode, але не змінити. Це зручний спосіб захистити важливі дані від випадкових змін.

Ще варіант: приватний set


public string Name { get; private set; }
Приватний сетер — змінюється лише всередині класу

Тоді властивість можна змінити лише всередині класу (наприклад, через метод або конструктор).

Лише для запису: сумнівна ідея

Теоретично можна зробити set-only властивість (тільки для запису), але це трапляється дуже рідко й не рекомендується — уявіть об’єкт, який поводиться як «чорна діра»: туди можна щось покласти, а дістати назад — ніколи.

4. init-only сетери

Коротка передісторія

Раніше, щоб створити незмінний об’єкт (immutable), зазвичай оголошували лише властивості з get і задавали значення через конструктор. Це зручно, але не завжди. Нещодавно в C# з’явився новий синтаксис: init-only властивості.

У чому суть init-сетера?

  • Дозволяє встановити значення властивості лише під час створення об’єкта (тобто у конструкторі або в ініціалізаторі об’єкта).
  • Після створення об’єкта властивість стає лише для читання.
  • Ідеально підходить для «майже незмінних» класів, DTO, конфігурацій і моделей.

public class Dog
{
    public string Name { get; init; }
    public int Age { get; init; }
}
Властивості з init-сетерами

Тепер ми можемо зробити так:

Dog dog = new Dog { Name = "Бобик", Age = 2 };
dog.Name = "Рекс"; // Помилка! Після створення - тільки читання

Значення також можна задавати в конструкторі:

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

Схема: коли можна присвоювати

Де присвоювати? { get; set; } { get; private set; } { get; init; }
Ззовні класу, після створення
У конструкторі
В ініціалізаторі
Усередині класу ❌ (крім конструктора)

5. Застосування на практиці

Давайте перепишемо наш клас Dog, використавши автоматичні та init-only властивості:


public class Dog
{
    // Властивість: встановлюється лише під час ініціалізації
    public string Name { get; init; }
    public int Age { get; init; }

    // Автоматична властивість для поточного стану
    public bool IsHungry { get; set; }

    // Метод: собака гавкає
    public void Bark()
    {
        Console.WriteLine($"{Name} каже: Гав!");
    }
}

Тепер можна створювати Dog ось так:

Dog dog = new Dog { Name = "Шарик", Age = 3, IsHungry = true };

dog.Bark(); // виведе: Шарик каже: Гав!
dog.IsHungry = false; // цю властивість можна змінювати після створення
dog.Name = "Барбос"; // Помилка! Властивість тільки для ініціалізації

6. Автоматичні властивості з початковими значеннями

У C# усе зроблено для вашої зручності. Можна навіть одразу задати початкове значення властивості:


public class Dog
{
    public string Name { get; set; } = "Безіменний";
    public int Age { get; set; } = 0;
}
Автоматичні властивості з початковими значеннями

Або з init:


public class Dog
{
    public string Name { get; init; } = "Щеня";
    public int Age { get; init; } = 0;
}
init-only властивості з початковими значеннями

Тепер, якщо не вказати Name під час створення, воно матиме значення «Щеня».

7. Автоматичні властивості з різним рівнем доступу

Можна зробити так, щоб властивість читалася й записувалася з різними модифікаторами доступу:


public class Dog
{
    public string Name { get; private set; }
    public int Age { get; private set; }
    
    public Dog(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

У цьому випадку змінити імʼя й вік можна лише всередині класу Dog, наприклад, у методах або конструкторі.

Порівняння ручних і автоматичних властивостей

Підхід Складність написання Контроль у set/get Змінюваність після створення Використання в моделях
Ручна властивість Довго Повний Будь-яка Там, де потрібна логіка
Автоматична властивість Коротко Немає Будь-яка або приватна Майже всюди
Автоматична з init-only Коротко Немає Тільки при створенні DTO, конфігурації

8. Типові помилки при роботі з властивостями

Помилка № 1: спроба присвоїти значення get-only властивості поза конструктором.
Якщо властивість оголошена лише з get, компілятор не дозволить змінити її значення десь ще. Присвоювати можна лише в конструкторі або прямо під час оголошення. Забули — отримаєте помилку компіляції.

Помилка № 2: плутанина між полем і властивістю.
Коли замінюють звичайне поле на автоматичну властивість, важливо переконатися, що в іншому коді немає прямого звернення до поля. Поле і властивість — різні сутності, і якщо залишити старі звернення, компілятор або видасть помилку, або програма поводитиметься непередбачувано.

Помилка № 3: приватний set без розуміння наслідків.
Якщо у властивості є приватний set, а в іншому коді все ще намагаються присвоювати йому значення — буде помилка. Часто це трапляється випадково, особливо під час копіювання чужого коду. Завжди перевіряйте, чи доступний set там, де ви маєте намір його використовувати.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ