Приватна змінна та власність?


41

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

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

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

Відповіді:


23

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

У випадку 2 і 3 завжди перейдіть до Accessor Access (не змінна поля). І у випадку 1, ви врятуєтесь навіть від того, щоб зробити цей вибір.

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

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.) змінна POCO. Проста змінна, яку можна отримати / встановити в будь-якій публічній / приватній області. У цьому випадку я б просто застосував автоматичну властивість.

public class Abc
{ 
  public int Foo {get; set;}
}

3.) Властивості зв'язування ViewModel. Для класів, які підтримують INotifyPropertyChanged, я думаю, вам потрібна приватна змінна поле резервної копії.

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}

2
+1 для прикладу MVVM Насправді саме це викликало питання в першу чергу.
Едвард

4
+1: Змішайте 2/3 з AOP, і у вас є чудовий спосіб використання INPC. [Повідомляє] public int Foo {get; набір; }
Стівен Еверс

1
@Job Для будь-якого класу, що отримує доступ до класу, для незмінності достатньо приватного сетера. Однак всередині класу приватний сеттер не перешкоджає повторному встановленню значення після первинної побудови. Мовна функція на зразок "приватного набору для читання" може концептуально вирішити цю проблему, але вона не існує.
Шелдон Варкентін

1
Я новачок у C #, тому скажіть мені, навіщо використовувати public int Foo {get; set;}замість public int Foo?

1
Якщо клас або структура буде вести себе як POCO або PODS, яка реальна перевага полягає в обгортанні полів у властивостях? Я повністю розумію, що обгортання полів у властивостях корисно, коли клас або структура потребує, або, можливо, в майбутньому, для підтримки інваріантів щодо його вмісту (можливо, шляхом забезпечення оновлення інших об'єктів для їх відповідності), але якщо клас або структура вказує, що споживачі можуть записувати будь-які значення в будь-якому порядку без обмежень або побічних ефектів, яку корисну поведінку можливо додати до учасника-учасника?
supercat

18

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

Це, звичайно, не є фактором продуктивності. Оптимізатор вбудує для вас просте отримання або встановлення, і кінцевий код MSIL, ймовірно, буде ідентичним.


Якась конкретна причина використання поля в конструкторі? Менший шанс на дивні побічні ефекти?
Увійти

4
@ Підпис: Моя здогадка полягає в тому, що якщо у власності (зараз або в майбутньому) буде перевірка, ви не хочете ризикувати, що під час будівництва буде невдача перевірки. Перевірка на даному етапі не логічна, тому що об'єкт не може бути гарантований стабільним, поки конструктор не закінчиться.
Стівен Еверс

@ Підпис: І те, що ви сказали, і те, що сказав Снорфус. Або якщо я хочу зареєструвати зміни у властивості, я, ймовірно, не хочу входити в початковий параметр. Але я сказала "взагалі".
пдр

3
@Sign: проблема полягає в тому, що якщо встановлений метод властивості може бути замінений у підкласі, у вас може виникнути побічний ефект під час створення об’єкта або непослідовного об'єкта (тобто: переопределена властивість запрограмована таким чином, щоб не задавати жодного значення для те поле). Використання властивостей у конструкторах безпечно лише в тому випадку, якщо встановлений метод приватний або якщо клас запечатаний.
Дієго

4

Залежить.

Спочатку слід віддати перевагу автоматичним властивостям, коли це можливо:

public string MyValue {get;set;}

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

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


3
Мені також подобається public string MyValue {get; private set;}.
Робота

3

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

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

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


2

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

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


2

Я завжди користуюсь громадською власністю.

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

У вас є коментар щодо MVVM, що веде до цього питання, і я вважаю, що це ще важливіше при роботі з MVVM. Багато об’єктів піднімають PropertyChangeсповіщення налаштування, а інші об'єкти можуть підписатися на цю подію, щоб виконати якусь дію, коли змінити конкретні властивості. Якщо встановити приватну змінну, ці дії ніколи не виконуватимуться, якщо ви також не вручну піднімете PropertyChangedподію.


+1 Так, у більшості випадків (MVVM) подія PropertyChanged є обов'язковою. І це може бути звільнено лише у власності. Гарне пояснення.
Едвард

1

Як правило, від вас залежить, що ви повинні робити з властивістю та її резервним полем при отриманні / налаштуванні.

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

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


0

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

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

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

Але в інших ситуаціях ви можете просто використовувати властивість як перегляд моделі даних:

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

Якщо є сенс використовувати форму радіану SomeAngle, то, будь-якими способами, використовуйте його.

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

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