Частная переменная против свойства?


41

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

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

Существует ли соглашение, которое определяет, как мы должны присваивать значения переменным внутри наших классов? Например, если у меня есть метод внутри того же класса, я должен назначить его с помощью свойства или с помощью закрытой переменной. Я видел, как это происходит в обоих направлениях, поэтому мне было интересно, если это выбор или производительность является фактором (незначительным, вероятно).

Ответы:


23

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

В случаях 2 и 3 всегда переходите к свойству свойства (а не к переменной поля). И в случае 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, каково реальное преимущество оборачивания полей в свойствах? Я полностью понимаю, что перенос полей в свойствах полезен, когда класс или структура нуждаются или могут в будущем нуждаться в поддержке инвариантов в отношении своего содержимого (возможно, путем обеспечения обновления других объектов в соответствии с ними), но если класс или структура указывает, что потребители могут записывать любые значения в любом порядке без ограничений или побочных эффектов, какие полезные действия могут быть добавлены к элементу доступа участника?
суперкат

18

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

Это, конечно, не фактор производительности. Оптимизатор встроит простое получение или установку для вас, и окончательный код MSIL, вероятно, будет идентичным.


Есть какая-то конкретная причина использования поля в конструкторе? Меньше шансов на странные побочные эффекты?
Подпишите

4
@Sign: я предполагаю, что если есть свойство для проверки (сейчас или в будущем), вы не хотите подвергаться риску сбоя проверки во время строительства. На этом этапе проверка не логична, поскольку нельзя гарантировать стабильность объекта до завершения работы конструктора.
Стивен Эверс

@Sign: и то, что ты сказал, и то, что сказал Снорфус. Или, если я хочу регистрировать изменения в свойстве, я, вероятно, не хочу регистрировать начальную настройку. Но я сказал «в общем».
фунтовые

3
@Sign: проблема в следующем: если метод set свойства может быть переопределен в подклассе, вы можете получить побочный эффект при создании объекта или несогласованного объекта (т. Е. Переопределенное свойство запрограммировано так, чтобы не устанавливать никакого значения для это поле). Использование свойств в конструкторах безопасно только в том случае, если метод set является закрытым или класс закрыт.
Диего

4

Зависит.

Сначала вы должны предпочесть автоматические свойства, когда это возможно:

public string MyValue {get;set;}

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

Но вы также должны принять во внимание, что это может снизить вашу производительность (немного), если вы неправильно синхронизируете, вы можете заблокировать себя, и иногда правильный путь - обойти логику в свойстве.


3
Я также хотел public string MyValue {get; private set;}.
Работа

3

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

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

Вы просто должны рассуждать об этом концептуально. Свойство фактически является дескриптором для доступа к некоторому внутреннему состоянию объекта, которое может состоять из нескольких переменных-членов. Поэтому вы должны спросить себя, хотите ли вы изменить только базовое внутреннее состояние (или только его часть) или абстрактное свойство, представляющее это состояние в целом, и в большинстве случаев это действительно последнее, поскольку вы обычно хотите, чтобы ваш объект всегда имел последовательное состояние.


2

Если есть вероятность, что реализация этих свойств get / set будет иногда меняться позже (например, вы хотите вызвать событие при вызове 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, то обязательно используйте ее.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.