Чи є причини використовувати приватну власність у C #?


246

Я щойно зрозумів, що конструкція властивості C # також може бути використана з модифікатором приватного доступу:

private string Password { get; set; }

Хоча це технічно цікаво, я не можу уявити, коли б я ним скористався, оскільки приватне поле передбачає ще менше церемонії :

private string _password;

і я не можу уявити, коли мені колись потрібно мати можливість внутрішньо отримати, але не встановити або встановити, але не отримати приватне поле:

private string Password { get; }

або

private string Password { set; }

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

Хтось використовує приватні властивості в C # з будь-якої причини або це лише одна з тих технічно можливих, але дуже рідко використовуваних-фактичних-конструкцій коду?

Додаток

Приємні відповіді, читаючи їх, я вирішив ці можливості для приватної власності:

  • коли приватні поля потрібно ліниво завантажувати
  • коли приватні поля потребують додаткової логіки або обчислюються значеннями
  • оскільки приватні поля можуть бути важкими для налагодження
  • щоб "представити собі договір"
  • внутрішньо перетворити / спростити відкриту властивість як частину серіалізації
  • загортання глобальних змінних, які будуть використовуватися всередині вашого класу

Техніка натхнені приватної власності є саме інкапсуляція - см: sourcemaking.com/refactoring/self-encapsulate-field
LBushkin

можливий дублікат
Різниць

Відповіді:


212

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

private string _password;
private string Password
{
    get
    {
        if (_password == null)
        {
            _password = CallExpensiveOperation();
        }

        return _password;
    }
}

42
приємний загальний зразок для цьогоreturn _password ?? (_password = CallExpensiveOperation());
Марк

1
@EvAlex Lazy <T> може виявитися кращим, якщо ви можете використовувати його. Але він може використовувати лише статичні речі, тому ви не можете отримати доступ до (нестатичних) методів або інших членів. До речі, я віддаю перевагу синтаксису однорядних return _password ?? (_password = CallExpensiveOperation());з деякого часу. Або насправді Resharper віддає перевагу :).
Барт

4
@Marc з C # 6 вам навіть не потрібно писати отримання та повернення. private string Password => _password ?? (_password = CallExpensiveOperation());
Otto Abnormalverbraucher

1
З C # 8 це ще коротше:private string Password => _password ??= CallExpensiveOperation();
Дейв М

2
@Bas Це не ліниве завантаження, оскільки CallExpensiveOperation();викликається під час побудови / ініціалізації об'єкта, що містить, а не тоді, коли вперше доступ до властивості.
Стефан Підскубка

142

Основне використання цього в моєму коді - лінива ініціалізація, як уже згадували інші.

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

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


5
Чи є ваш код набагато потужнішим за дані? Гугл він повертає посилання, які вказують на вас. Просто хочу знати, щоб я міг правильно це цитувати, коли мені потрібно.
Джоан Венге

24
@Joan: Я не знаю. Або я придумав це, або почув, як хтось інший це сказав, і подумав, "ого, я повинен це повністю вкрасти, а потім забути про те, у кого я його вкрав".
Ерік Ліпперт

1
+ CodeLens повідомляє, куди посилається майно
UuDdLrLrSs

43

можливо, є випадок використання з вкладеними / успадкованими класами або, можливо, де get / set може містити логіку, а не просто повертати значення властивості

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

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


У напівзв’язаному випадку (хоча він відрізняється від вашого запитання), я дуже часто використовую приватні налаштування громадських ресурсів:

public string Password 
{
    get; 
    private set;
}

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


+1 має сенс: "Якщо я відчуваю, що власність може врешті-решт вимагати додаткової логіки, я іноді загортаю її в приватну власність замість того, щоб використовувати поле, просто так, що мені не доведеться пізніше змінювати код".
Едвард Тангуай

7
приватні сетери <3
Earlz

20

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


20

Ледача ініціалізація - це одне місце, де вони можуть бути акуратними, наприклад

private Lazy<MyType> mytype = new Lazy<MyType>(/* expensive factory function */);

private MyType MyType { get { return this.mytype.Value; } }

// In C#6, you replace the last line with: private MyType MyType => myType.Value;

Тоді ви можете написати: this.MyTypeскрізь, а не this.mytype.Valueі капсулюйте той факт, що він ліниво описується в одному місці.

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


2
Погодився, що це буде мати аспект масштабування там.
Кріс Марісіч

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

5
@Eric Lippert - field-declarations accessor-declarationsвже давно займає номер 1 у моєму списку побажань C #. Якщо ви змогли отримати те, що було розроблено та впроваджено в деякій (фактичній) майбутній версії, то я спечу вам торт.
Джефрі Л Уітлідж

13

Єдине використання, яке я можу придумати

private bool IsPasswordSet 
{ 
     get
     {
       return !String.IsNullOrEmpty(_password);
     }
}

+1 за клас корисних властивостей, які обчислюються з інших приватних змінних
Дарен Томас,

2
Чому б не скористатися приватним методомprivate bool IsPasswordSet() { return !String.IsNullOrEmpty(_password); }
Роман

10

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

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


+1: "Ви також можете представити собі контракт з дуже схожих причин" має сенс
Едвард Тангуай

8

Я використовую їх у серіалізації з такими речами, як DataContractSerializerпротобуф-нетто, які підтримують це використання ( XmlSerializerне). Це корисно, якщо вам потрібно спростити об’єкт у рамках серіалізації:

public SomeComplexType SomeProp { get;set;}
[DataMember(Order=1)]
private int SomePropProxy {
    get { return SomeProp.ToInt32(); }
    set { SomeProp = SomeComplexType.FromInt32(value); }
}

6

Одне, що я роблю весь час, - це зберігати "глобальні" змінні / кеш в HttpContext.Current

private static string SomeValue{
  get{
    if(HttpContext.Current.Items["MyClass:SomeValue"]==null){
      HttpContext.Current.Items["MyClass:SomeValue"]="";
    }
    return HttpContext.Current.Items["MyClass:SomeValue"];
  }
  set{
    HttpContext.Current.Items["MyClass:SomeValue"]=value;
  }
}

5

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

Також може бути корисним, якщо вам пізніше потрібно якимось чином змінити тип своїх даних або якщо вам потрібно використовувати відображення.


Дітто; якщо в get / set є логіка, я іноді можу використовувати приватну або захищену власність. Зазвичай залежить від того, скільки логіки: просту логіку я буду робити у властивості, багато логіки, як правило, я буду використовувати допоміжну функцію. Що б не зробило код найбільш ретельним.
TechNeilogy

5

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

    private double MonitorResolution
    {
        get { return this.Computer.Accesories.Monitor.Settings.Resolution; }
    }

Це корисно, якщо є багато суб властивостей.


2

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


2

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

Якщо це просто пряме опорне поле? Нічого не спадає на думку як вагома причина.


2

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

Я не уявляю, коли мені колись потрібно мати можливість внутрішньо дістати, але не налаштувати

Якщо ви вводите свої залежності, можливо, ви хочете мати Getter на властивості, а не сеттер, оскільки це буде означати властивість лише для читання. Іншими словами, Властивість може бути встановлена ​​лише в конструкторі і не може бути змінена будь-яким іншим кодом у класі.

Також Visual Studio Professional надасть інформацію про властивість, а не про поле, що полегшить зрозуміти, для чого використовується ваше поле.

PorpField


1

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

  • Перевірка

    string _password;
    string Password
    {
        get { return _password; }
        set
        {
            // Validation logic.
            if (value.Length < 8)
            {
                throw new Exception("Password too short!");
            }
    
            _password = value;
        }
    }
  • Блокування

    object _lock = new object();
    object _lockedReference;
    object LockedReference
    { 
        get
        {
            lock (_lock)
            {
                return _lockedReference;
            }
        }
        set
        {
            lock (_lock)
            {
                _lockedReference = value;
            }
        }
    }

    Примітка. При блокуванні посилання ви не блокуєте доступ до членів посилального об'єкта.

Лінивий довідник: Коли ледаче завантаження вам може виникнути потреба зробити це асинхронізацією, для якої зараз існує AsyncLazy . Якщо ви використовуєте старіші версії, ніж Visual Studio SDK 2015, або не використовуєте їх, ви також можете використовувати AsyncEx AsyncLazy .


0

Ще кілька екзотичних видів використання явних полів включають:

  • вам потрібно використовувати refабо outзі значенням - можливо, тому, що це Interlockedлічильник
  • він призначений для подання основного макета, наприклад, на structявному макеті (можливо, для зіставлення на C ++-дамп або unsafeкод)
  • історично тип використовувався з BinaryFormatterавтоматичною обробкою поля (перехід на автоматичну реквізит змінює назви і, таким чином, порушує серіалізатор)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.