1. Вступ
Почнімо з проблем, що виникають під час прямого використання полів. Якщо ви оголосите поле як public, його можна буде змінювати одразу й напряму з будь-якої частини програми:
public class Dog
{
public string Name;
}
Dog dog = new Dog();
dog.Name = ""; // O_o ... це як "імʼя собаки = порожній рядок"!
Користувач може присвоїти полю Name будь‑які, навіть абсурдні, значення: порожній рядок, надто довге імʼя або взагалі null. Це все одно, що хтось збирав би вашу нову шафу IKEA, а ви дали б йому повний доступ до деревообробного цеху.
Памʼятайте: інкапсуляція — це коли обʼєкт сам контролює свої дані. Ми не довіряємо внутрішнє кожному охочому, а даємо спеціальні «дверцята» — властивості (Properties), через які й відбувається доступ до поля — із можливістю перевірки, журналювання, модифікації чи інших реакцій на зміну значення.
2. Визначення і синтаксис
Властивість — це особливий член класу, який виглядає майже як поле, але насправді всередині є парою спеціальних методів: getter (отримати значення) і setter (встановити значення). За допомогою властивості ми можемо:
- Дозволити або заборонити читання чи запис даних;
- Додати валідацію або логіку під час доступу до даних;
- Сховати внутрішнє поле й навіть зберігати значення деінде.
Властивість оголошується дуже схоже на поле, тільки з фігурними дужками і ключовими словами get і set усередині.
[модифікатор_доступу] тип ІмʼяВластивості
{
get { ... }
set { ... }
}
Ось приклад для нашого класу Dog:
public class Dog
{
private string _name; // внутрішнє поле (private!)
public string Name
{
get { return _name; } // "геттер": отримати імʼя
set { _name = value; } // "сеттер": присвоїти імʼя
}
}
Примітка: підкреслення зазвичай використовують для приватних полів (_name). Це поширений стиль у C#.
3. Механіка роботи властивості
Властивість — це свого роду «охоронець», який стоїть між внутрішніми даними обʼєкта і зовнішнім світом. Приклад:
Dog dog = new Dog();
dog.Name = "Рижик";
Console.WriteLine(dog.Name);
Пояснення:
- Коли виконання програми дістається рядка dog.Name = "Рижик";, викликається set-метод властивості, і ви можете додати туди будь‑яку потрібну перевірку (наприклад, переконатися, що імʼя не порожнє).
- У момент Console.WriteLine(dog.Name); викликається get-метод, який просто повертає поточне значення або, за потреби, обчислює його динамічно.
Виглядає, наче це звичайне поле, але насправді тут усе під контролем!
4. Чому властивості — це «найкраща практика»
У більшості випадків ми не надаємо прямого доступу до внутрішніх полів обʼєкта. Навіть якщо зараз перевірки не потрібні, звичка огортати дані у властивості дуже виручає, коли правила гри зміняться.
Приклад перевірки під час присвоєння:
public class Dog
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (string.IsNullOrWhiteSpace(value))
{
throw new ArgumentException("Імʼя собаки не може бути порожнім!");
}
_name = value;
}
}
}
Тепер таке присвоєння:
dog.Name = ""; // Буде викинуто виняток!
… захищає наш обʼєкт від абсурдних значень.
5. Властивості: лише для читання, лише для запису і звичайні
Іноді варто дозволяти лише читання значення (наприклад, у собаки є рік народження — його не змінюють) або, навпаки, лише встановлення (зустрічається рідко, але інколи це потрібно).
- Лише для читання: пишемо тільки get, прибираємо set.
- Лише для запису: пишемо тільки set, прибираємо get.
Приклади:
public class Dog
{
private int _birthYear = 2018;
// Лише для читання
public int BirthYear
{
get { return _birthYear; }
}
// Лише для запису (зустрічається рідко)
public string Secret
{
set { /* робимо щось з value */ }
}
}
6. Властивості проти полів
| Поле | Властивість | |
|---|---|---|
| Синтаксис | |
|
| Доступ | Прямий | Через get/set |
| Валідація | Немає | Можна додати у set/get |
| Розширюваність | Немає | Можна модифікувати в будь-який момент |
| IDE-інтеграція | Видно як поля | Видно як властивості (важливо для фреймворків) |
Ілюстрація: Робота властивості
sequenceDiagram
participant User as Користувач об'єкта
participant Dog as Об'єкт Dog
participant Field as Приватне поле _name
User->>Dog: dog.Name = "Рижик"
Dog->>Dog: set Name("Рижик")
Dog->>Field: _name = "Рижик"
User->>Dog: print(dog.Name)
Dog->>Dog: get Name()
Dog->>Field: читає _name
Dog->>User: повертає "Рижик"
7. Типові помилки і нюанси
Розберімося, де тут можуть підстерігати пастки.
Типова помилка — плутати поля й властивості та випадково відкривати приватні дані назовні:
public string name; // Це поле! Його видно всюди, небезпечно!
Краще так:
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
Статичні властивості існують, щоб зберігати спільні для всіх обʼєктів значення. Але використовуйте їх обережно.
Цікаво: якщо у властивості є лише get і немає set, її неможливо змінити — це називається незмінна властивість (immutable property) і активно використовується в сучасних підходах до проєктування (ми детально повернемося до цього в лекціях про record і незмінність даних).
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ