Автоматично реалізовані геттери та сетери проти загальнодоступних полів


76

Я бачу багато прикладу коду для класів C #, який робить це:

public class Point {
    public int x { get; set; }
    public int y { get; set; }
}

Або, у старому коді, те саме з явним приватним значенням резервної копії та без нових властивостей, які автоматично реалізуються:

public class Point {
    private int _x;
    private int _y;

    public int x {
        get { return _x; }
        set { _x = value; }
    }

    public int y {
        get { return _y; }
        set { _y = value; }
    }
}

Моє питання - чому. Чи існує якась функціональна різниця між виконанням вищезазначеного і тим, щоб зробити цих учасників загальнодоступними полями, як показано нижче?

public class Point {
    public int x;
    public int y;
}

Щоб бути зрозумілим, я розумію цінність геттерів та сеттерів, коли вам потрібно зробити переклад базових даних. Але у випадках, коли ви просто передаєте значення, це здається зайвим багатослівним.

Відповіді:


50

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

Джефф Етвуд впорався з цим кілька років тому. Найважливіший момент, який він ретроспективно зазначив, полягає в тому, що перехід від поля до властивості - це невід'ємна зміна вашого коду; все, що споживає його, має бути перекомпільовано для роботи з новим інтерфейсом класу, тому, якщо щось, що не входить до вашого контролю, споживає ваш клас, у вас можуть виникнути проблеми.


Дякую за це посилання та переконую мене у тому, що насправді справи перервуться у переході поля / майна. Для мене це дивно, оскільки синтаксис однаковий незалежно.
tclem

Можна подумати, що це складеться до одного і того ж, але, очевидно, внутрішня механіка дещо інша.
Декстер

9
У C # властивість із get і set компілюється до пари методів, таких як get_PropertyName () та set_PropertyName (значення типу). Синтаксис властивостей у C # - це просто синтаксичний цукор. Властивості можуть виконувати код, тому не має сенсу компілювати їх до того самого коду, що і поле.
Джейсон Джексон,

9
Компілятори JIT оптимізують властивість без коду, що еквівалентно полю, тому немає причин не використовувати властивості, враховуючи скорочений синтаксис у C # 3.0+
Erv Walter

31

Також набагато простіше змінити його на це пізніше:

public int x { get; private set; }

11
Я ... навіть не знав, що ти можеш це зробити.
Merus

Правда, це дуже корисно, на жаль, цього не можна зробити в інтерфейсі
Жорж,

9

Він інкапсулює встановлення та доступ цих членів. Якщо через деякий час розробник коду повинен змінити логіку при доступі або встановленні члена, це можна зробити, не змінюючи контракту класу.


1
Але як це змінює контракт класу? У будь-якому випадку, point.x = 1;і point.xпрацювати просто чудово.
Ajedi32,

2
@ Ajedi32 технічно це не один контракт. Якщо ви змінили його з поля на властивість (або властивість на поле), вам доведеться перекомпілювати все, що має доступ до цього члена. Оскільки скомпільований ІЛ відрізняється при зверненні до властивості та поля.
Джозеф Дейгл

8

Ідея полягає в тому, що навіть якщо структуру даних, що лежить в основі, потрібно змінити, загальнодоступний інтерфейс класу не доведеться змінювати.

C # може по-різному трактувати властивості та змінні. Наприклад, ви не можете передавати властивості як параметри ref або out . Отже, якщо вам потрібно змінити структуру даних з якихось причин, і ви використовували загальнодоступні змінні, і тепер вам потрібно використовувати властивості, ваш інтерфейс доведеться змінити, і тепер код, який звертається до властивості x, може більше не компілюватися, як коли він був змінною x:

Point pt = new Point();
if(Int32.TryParse(userInput, out pt.x))
{
     Console.WriteLine("x = {0}", pt.x);
     Console.WriteLine("x must be a public variable! Otherwise, this won't compile.");
}

Використання властивостей з самого початку уникає цього, і ви можете сміливо налаштовувати базову реалізацію стільки, скільки вам потрібно, не порушуючи клієнтський код.


3

Setter і Getter дозволяють додавати додатковий рівень абстракції, і в чистому ООП ви завжди повинні отримувати доступ до об'єктів через інтерфейс, який вони надають зовнішньому світу ...

Розгляньте цей код, який врятує вас на asp.net і який не був би можливим без рівня абстракції, який надають сетери та геттери:

class SomeControl
{

private string _SomeProperty  ;
public string SomeProperty 
{
  if ( _SomeProperty == null ) 
   return (string)Session [ "SomeProperty" ] ;
 else 
   return _SomeProperty ; 
}
}

3

Оскільки автоматично реалізовані геттери приймають одне і те ж ім'я для властивості та фактичних змінних приватного сховища. Як ви можете змінити це в майбутньому? Я думаю, що йдеться про те, що замість поля використовуйте реалізований auto, щоб ви могли змінити його в майбутньому, якщо у випадку, якщо вам потрібно додати логіку до getter та setter.

Наприклад:

public string x { get; set; }

і, наприклад, ви вже використовуєте xa багато разів і не хочете зламати свій код.

Як ви змінюєте автоматичний сеттер-сетер ... наприклад, для сеттера ви дозволяєте лише встановити дійсний формат телефонного номера ... як ви змінюєте код, щоб змінювався лише клас?

Моя ідея полягає в тому, щоб додати нову приватну змінну та додати ті самі x getter та setter.

private string _x;

public string x { 
    get {return _x}; 
    set {
        if (Datetime.TryParse(value)) {
            _x = value;
        }
    }; 
}

Це ви маєте на увазі, роблячи його гнучким?


2

Також слід враховувати ефект зміни для членів громадськості, коли йдеться про обов'язковість та серіалізацію. Обидва вони часто покладаються на загальнодоступні властивості для отримання та встановлення значень.


2

Крім того, ви можете встановлювати точки зупинки на геттерах і сеттерах, але не можете на полях.


1

AFAIK згенерований інтерфейс CIL відрізняється. Якщо ви змінили загальнодоступного члена на властивість, ви змінюєте його загальнодоступний інтерфейс, і вам потрібно відновити кожен файл, який використовує цей клас. Це не потрібно, якщо ви лише змінюєте реалізацію геттерів і сеттерів.



1

Варто також зазначити, що ви не можете зробити Auto Properties лише для читання і не можете ініціалізувати їх вбудованими. Обидва ці речі я хотів би бачити у майбутньому випуску .NET, але я вважаю, що ви не можете зробити ні те, ні інше в .NET 4.0.

Єдиний раз, коли я використовую поле резервного копіювання із властивостями в наші дні, це коли мій клас реалізує INotifyPropertyChanged, і мені потрібно запускати подію OnPropertyChanged при зміні властивості.

Крім того, у цих ситуаціях я встановлюю поля для резервного копіювання безпосередньо, коли значення передаються з конструктора (не потрібно намагатися запускати OnPropertyChangedEvent (який би в цей час був NULL), де б я ще не використовував саме властивість.


2
Це не зовсім так, ви можете мати автоматично реалізовані властивості лише для читання (або лише для запису), якщо у вас є SomePropName {get; приватний набір;}, а потім встановити властивість за допомогою ініціалізаторів об’єктів ..
jhexp

0

Ви ніколи не знаєте, чи не знадобиться вам пізніше переклад даних. Ви готові до цього, якщо приховуєте своїх членів. Користувачі вашого класу не помічають, якщо ви додаєте переклад, оскільки інтерфейс залишається незмінним.


0

Найбільша різниця полягає в тому, що якщо ви коли-небудь зміните свою внутрішню структуру, ви все одно зможете підтримувати геттери та сеттери як є, змінюючи їх внутрішню логіку, не завдаючи шкоди користувачам вашого API.


0

Якщо вам доведеться змінити спосіб отримання x та y у цьому випадку, ви можете просто додати властивості пізніше. Це я вважаю найбільш заплутаним. Якщо ви використовуєте загальнодоступні змінні-члени, ви можете легко змінити це на властивість пізніше і використовувати приватні змінні _x та _y, якщо вам потрібно зберігати значення всередині.


0

чому ми не просто використовуємо загальнодоступні поля замість того, щоб використовувати властивості, а потім викликати засоби доступу (get, set), коли нам не потрібно робити перевірки?

  1. Властивість - це член, який забезпечує гнучкий механізм лише для читання або лише запису
  2. Властивості можна замінити, але поля - не.

0

Додавання getter і setter робить змінну властивістю, як при роботі в Wpf / C #.

Якщо це просто загальнодоступна змінна члена, вона не доступна з XAML, оскільки вона не є властивістю (навіть якщо її відкрита змінна члена).

Якщо він має сеттер і геттер, то його можна отримати з XAML, оскільки тепер він є властивістю.


-1

Сеттери та геттери в принципі погані (вони погано пахнуть ОО - я зупинюсь, не кажучи, що вони є анти-шаблоном, тому що вони дійсно іноді потрібні).

Ні, технічно різниці немає, і коли я справді хочу поділитися доступом до об’єкта в наші дні, я час від часу роблю його загальнодоступним остаточним, замість того, щоб додавати геттер.

Спосіб, як сетери та геттери були «продані», полягає в тому, що вам, можливо, доведеться знати, що хтось отримує значення або змінює його - що має сенс лише у примітивів.

Об'єкти мішків властивостей, такі як DAO, DTO та об'єкти відображення, виключаються з цього правила, оскільки це не об'єкти у реальному значенні слова "Об'єкт". (Ви не думаєте про "передачу повідомлень" DAO, це просто купа пар атрибут / значення).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.