Есть ли причины использовать частные свойства в 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());с тех пор. Или на самом деле Решарпер предпочитает это :).
Барт

4
@ Marc начиная с C # 6 вам даже не нужно писать get и return. private string Password => _password ?? (_password = CallExpensiveOperation());
Отто Аномалвербраухер,

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

2
@Bas Это не ленивая загрузка, потому что CallExpensiveOperation();вызывается во время построения / инициализации содержащего объекта, а не при первом обращении к свойству.
Стефан Подскубка

142

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

Другая причина закрытых свойств над полями состоит в том, что частные свойства гораздо, гораздо проще отлаживать, чем частные поля. Я часто хочу знать такие вещи, как «это поле неожиданно устанавливается; кто первый, кто устанавливает это поле?» и это намного проще, если вы можете просто поставить точку останова на сеттере и нажать кнопку go. Вы можете поставить вход в систему там. Вы можете поместить показатели производительности там. Вы можете включить проверки согласованности, которые выполняются в отладочной сборке.

По сути, это сводится к следующему: код гораздо более мощный, чем данные . Любая техника, которая позволяет мне писать нужный мне код, хороша. Поля не позволяют писать код в них, свойства делают.


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

24
@Жоан: я не знаю Либо я выдумал, либо услышал, как кто-то сказал это, и подумал: «Ух ты, я должен полностью украсть это, а потом забыть все о том, у кого я его украл».
Эрик Липперт

1
+ CodeLens сообщает вам, на что ссылается свойство
UuDdLrLrSs

43

возможно, есть сценарий использования с вложенными / унаследованными классами или, возможно, где get / set может содержать логику, а не просто возвращать значение свойства

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

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


В полу-связанном случае (хотя и отличающемся от вашего вопроса) я очень часто использую частные сеттеры для открытых свойств:

public string Password 
{
    get; 
    private set;
}

Это дает вам общедоступный метод получения, но сохраняет его в частном порядке.


+1 имеет смысл: «Если я чувствую, что свойство может в конечном итоге потребовать дополнительной логики, я иногда заключаю его в частное свойство вместо использования поля, просто чтобы позже мне не пришлось менять свой код».
Эдвард Тангей

7
частные сеттеры <3
Эрлз

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 - soped в field-declarationрамках accessor-declarationsзанимает долгое время номер 1 в моем списке пожеланий C #. Если бы вы могли спроектировать и реализовать это в какой-то (реальной) будущей версии, то я испеку вам торт.
Джеффри Л Уитледж

13

Единственное использование, которое я могу придумать

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

+1 за полезный класс свойств, которые вычисляются из других частных переменных
Дарен Томас

2
Почему бы не использовать приватный методprivate bool IsPasswordSet() { return !String.IsNullOrEmpty(_password); }
Roman

10

Свойства и поля не один в один. Свойство относится к интерфейсу класса (говорит о его открытом или внутреннем интерфейсе), а поле - к реализации класса. Свойства не должны рассматриваться как способ просто раскрыть поля, они должны рассматриваться как способ раскрыть намерение и цель класса.

Точно так же, как вы используете свойства, чтобы представить своим клиентам контракт на то, что составляет ваш класс, вы также можете представить контракт себе по очень похожим причинам. Так что да, я использую частные свойства, когда это имеет смысл. Иногда частное свойство может скрывать детали реализации, такие как отложенная загрузка, тот факт, что свойство действительно представляет собой конгломерат нескольких полей и аспектов, или что свойство должно быть виртуально создано с каждым вызовом (подумайте DateTime.Now). Определенно бывают моменты, когда имеет смысл применять это даже в бэкэнде класса.


+1: «Вы также можете представить себе контракт по очень схожим причинам»
Эдвард Тангей

8

Я использую их в сериализации, с такими вещами, как DataContractSerializerили protobuf-net, которые поддерживают это использование ( 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

Это имеет смысл, когда есть логика, связанная со свойством set или get (подумайте, ленивая инициализация), и свойство используется в нескольких местах в классе.

Если это просто прямое поле поддержки? Ничто не приходит на ум в качестве веской причины.


2

Я знаю, что этот вопрос очень старый, но информации, приведенной ниже, не было ни в одном из текущих ответов.

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

Если вы вводите свои зависимости, вы, возможно, захотите иметь Getter для Property, а не setter, поскольку это будет означать свойство только для чтения. Другими словами, свойство может быть установлено только в конструкторе и не может быть изменено любым другим кодом в классе.

Кроме того, 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, или не используете его, вы также можете использовать AsyncLazy от AsyncEx .


0

Некоторые более экзотические использования явных полей включают в себя:

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