Разница между переменной-членом и элементом-членом?
Существуют ситуации, когда я объявляю переменные-члены в верхней части моего класса, а затем объявляю свойство для доступа или установки этой переменной-члена, но я спрашиваю себя, нужно ли свойство, если он будет доступен только для переменной, и установленный внутри класса и не где-то еще, так что в чем преимущество использования свойства для доступа и установки переменной-члена вместо того, чтобы просто делать это непосредственно с самой переменной-членом. Вот пример:
public class Car
{
int speed; //Is this sufficient enough if Car will only set and get it.
public Car(int initialSpeed)
{
speed = initialSpeed;
}
//Is this actually necessary, is it only for setting and getting the member
//variable or does it add some benefit to it, such as caching and if so,
//how does caching work with properties.
public int Speed
{
get{return speed;}
set{speed = value;}
}
//Which is better?
public void MultiplySpeed(int multiply)
{
speed = speed * multiply; //Line 1
this.Speed = this.Speed * multiply; //Line 2
//Change speed value many times
speed = speed + speed + speed;
speed = speed * speed;
speed = speed / 3;
speed = speed - 4;
}
}
В приведенном выше примере, если у меня нет свойства Speed для установки и получения переменной скорости, и я решаю изменить скорость int на int spd, мне придется менять скорость на spd везде, где она используется, если я использую свойство, такое как Speed для установки и получения скорости, мне просто нужно будет изменить скорость на spd в get и set свойства, так что в моем методе MutilplySpeed, как и выше. Скорость = this.Speed + this.Speed + this.Speed не сломается.
Ответы
Ответ 1
Если переменная private
, я часто не создаю для нее свойство. Если он каким-либо образом подвергается внешнему типу, я всегда выставляю его через свойство по разным причинам:
- Это может быть и не нужно сегодня, но если это понадобится позже, это будет нарушающее изменение
- Работа с данными работает только по свойствам, а не по полям (я думаю, не большой пользователь привязки данных).
- Он позволяет вставлять валидации, протоколирование, точки останова при доступе к значению
Кроме того, если поле открыто через свойство, я всегда получаю его через свойство даже внутри класса.
Обновление
В ответ на ваши обновленные образцы кода: здесь есть несколько вещей, которые можно рассмотреть вокруг дизайна кода.
- Считываемость против скорости
- "атомарность"
- Другие побочные эффекты
Один типичный совет (который я нахожу очень хорошим) - это "писать для ясности, тест на производительность". Это означает, что когда вы пишете свой код, ваша первая проблема заключается в том, ясно ли, что делает код при просмотре. Это часто (но не всегда) более важно, чем необработанная скорость кода. Оптимизация скорости записи, когда вы установили, где вы ее получите. Доступ к собственности будет чуть медленнее, чем прямое чтение поля, но в большинстве случаев разница будет незначительной (если вообще измерима).
Атомность может быть проблемой. Учитывая ваш пример кода, у нас есть поле speed
, которое открыто открыто через свойство speed
. Если метод MultiplySpeed
должен выполнить несколько обновлений для значения, эти промежуточные значения будут доступны через свойство speed
в разное время, пока расчет продолжается. Это верно независимо от того, обновляете ли вы поле напрямую или через свойство. В таких случаях, возможно, лучше сначала поместить значение в локальную переменную, использовать это для вычислений и присвоить значение этой переменной, когда это будет сделано.
Наконец, другие побочные эффекты. Возможно, изменение значения speed
должно привести к событию (например, SpeedChanged
). В подобных случаях также вероятно хорошая идея не делать обновление до тех пор, пока расчет не будет выполнен.
Мне нравится думать о собственности как о контракте, а поле - как о реализации. Любой (за исключением ядра моего типа), который нуждается в стоимости, должен использовать контракт. Опираясь на реализацию следует делать только в том случае, если есть достаточные основания для обхода контракта.
И да, если вы инкапсулируете доступ к полю в свойстве, естественно, изменение имени поля потребует меньшего количества обновлений (и, возможно, также имя поля становится менее важным).
Я надеюсь, что это имеет смысл, и не слишком много от темы;)
Ответ 2
Я согласен с ответом Фредерика. Одна вещь, которая делает его немного меньше работы, чтобы следовать его совету, - использовать автоматические свойства. Это просто свойства, которые автоматически генерируют стандартную логику getter/setter. Вы не получаете никакой проверки, но вы всегда можете заменить автоматическое свойство стандартным позже. Эта замена не является нарушением изменений.
Здесь я заменил свойство Speed в вашем примере автоматическим свойством. Обратите внимание, что переменная-член исчезает, и ваш класс должен получить к ней доступ через свойство.
public class Car
{
public Car(int initialSpeed)
{
Speed = initialSpeed;
}
public int Speed { get; set; }
public void MultiplySpeed(int multiply)
{
Speed *= multiply;
}
}
Вы также можете использовать другой аромат, называемый "get with private set". Это означает, что геттер является общедоступным, но сеттер является закрытым. Вы определяете его следующим образом:
public int Speed { get; private set; }
Что касается вашего вопроса о префиксе this.
, это обычно не имеет значения. Единственный раз, когда это важно, - это когда вы определили параметр метода или локальную переменную с тем же именем, что и переменная-член. Затем вы можете использовать this
для доступа к переменной-члену.
Ответ 3
Такие вещи, как проверка, могут быть покрыты в одном месте. Член инкапсулирован, и вы хотите беспокоиться о валидации и других вещах от остальной части вашего класса.
В вашем текущем сценарии это не имеет особого значения, но когда вам нужно изменить переменную или добавить поведение, проще использовать свойства, потому что у вас есть только одно место, где вам нужно его изменить.
Ответ 4
он не добавляет кеширование, но позволяет согласованный интерфейс.
Представьте, что вам нужно было ускорить скорость, добавив в нее постоянное значение в будущем. использование переменной-члена было бы трудным, когда свойство допускает эту манипуляцию.
Также внутри класса вы должны снова получить доступ к свойству для согласованности (представьте себе описанный выше сценарий, когда у вас был класс, который обращался к переменной-члену напрямую).
Ответ 5
Единственная истинная причина, по которой я знаю, - это доступ к полю из-за пределов сборки. В этом случае, если вы хотите добавить легкие функциональные возможности в это поле (возможно, установите флаг Dirty или подтвердите изменение), вы должны изменить его на свойство, которое изменяет способ его просмотра вызывающей сборкой, что также потребует перестройки, В очень редких случаях вы можете обнаружить, что у вас нет контроля над этой сборкой, тогда у вас есть проблема.
Не позволяйте фанатам OO сообщать вам, что это филсофически неправильно использовать публичные поля, хотя я могу согласиться с тем, что авто-свойства делают аргумент несколько спорным.
Ответ 6
Дело в том, что не существует большой разницы между публично объявленным полем и публичным свойством с частным хранилищем резервных копий, если нет дополнительной логики. Тем не менее, считается, что использовать свойства можно по-прежнему.
И прежде чем все скажут мне о расширяемости, помните, что если вам нужно будет добавить функциональность, вы можете сохранить имя с этим свойством и ввести новое имя для хранилища резервных копий, чтобы оно не изменилось.
Ответ 7
Одна вещь, о которой вы забыли упомянуть, свойства помогут вам, когда вы расширяете свой класс.
Если ваш класс правильно спроектирован, ваши переменные внутри базового класса должны быть private
. Без свойств свойств public
, которые есть. У вас не было бы доступа к этим частным переменным из расширенного класса. Мы говорим об общественном и частном, и я не включаю защиту по какой-то причине:).
Только некоторые заметки, которые стоит упомянуть:
- Свойства aide при расширении класса
- свойства для меня делают код более читаемым (в дополнение this.privateVariable verson PublicPropertyVariableName)
- свойства могут обеспечивать только чтение, частные наборы, общедоступные и т.д. (гораздо более читаемые для других программистов). Рассмотрим случай, когда идентификатор требует публичный доступ, но частный набор
- Лично мне слишком много get/sets, похоже, усложняет код, делает код менее читаемым, слишком много лишнего синтаксиса
- Наследование/расширение до расширенного класса не позволяет наследовать частные переменные, свойства - это ответ. (опять же упоминание о защите здесь, это совсем другая история).
- Для меня, даже если класс имеет приватную переменную, мои методы класса все еще используют свойство для доступа или использования этой закрытой переменной
- Не забывайте о проверке, это упрощает проверку правильности читаемости.
Это всего лишь некоторые общие вещи (мои 2 цента, хотя на большинстве из них).
Ответ 8
По-моему, дизайн языка нарушен. Не должно быть двух способов делать вещи с таким большим количеством семантического перекрытия. Свойства/Поля должны были легко обеспечить преимущества любого подхода в зависимости от того, как они используются. Если программа делает минимальное использование свойств Property, они должны действовать так же, как поля. Кроме того, не должно быть необходимости объявлять пустой get; и установить; методов в этом случае. Различия кажутся мне искусственными.
Это отличный язык; и довольно чистый по большей части. Это не значит, что в следующий раз его не следует улучшать.