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 там, де ви маєте намір його використовувати.

2
Задача
C# SELF, 17 рівень, 2 лекція
Недоступна
Використання init-only властивостей
Використання init-only властивостей
2
Задача
C# SELF, 17 рівень, 2 лекція
Недоступна
Автоматичні властивості з початковими значеннями
Автоматичні властивості з початковими значеннями
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ