Допустим есть это:
private int a { get; set; }
Какой в этом смысл, если я могу сделать так:
public int a;
Ответы
Ответ 1
Смотрите, какие есть преимущества у свойства перед полем.
Если ваше свойство определено так:
public int A { get; set; }
— то непосредственных выгод, конечно, нету. Но выгоды придут позже.
Вы можете навесить свою логику на запись и считывание значения. Применений може
быть море. Например, вы хотите посчитать, сколько раз считывалось значение:
private int a;
private int readcount_a = 0;
public int A
{
get { readcount_a++; return a; }
set { a = value; }
}
Вы можете сделать триггер на изменение поля:
class Data : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int a;
public int A
{
get { return a; }
set
{
if (a == value)
return;
a = value;
RaisePropertyChanged();
}
}
private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Вы можете залогировать все изменения поля:
public int A
{
get { return a; }
set
{
Trace.TraceInformation("Changing value of a from {0} to {1}", a, value);
a = value;
}
}
Вы можете навесить проверку значения на корректность при записи, или ленивую инициализаци
при чтении.
class Data
{
private string a = null;
public string A
{
get
{
return a ?? (a = LazyComputeInitialA());
}
set
{
if (value == null)
throw new ArgumentException(nameof(A) + " cannot be null", nameof(A));
a = value;
}
}
}
В конце-концов, вы можете не расходовать память на значение, если в большинстве случае
оно одинаковое (как это сделано у DependencyProperty):
class Data
{
static Dictionary aValues = new Dictionary();
public int A
{
get
{
int result;
if (aValues.TryGetValue(this, out result))
return result;
else
return -1; // default value
}
set
{
aValues[this] = value;
}
}
}
Вы хотите сделать разную степень видимости у геттера и сеттера, так что наприме
только ваш класс и его наследники смогут установить значение, а считать смогут все
(Это, пожалуй, самое лучшее применение свойств, на мой вкус.)
public int A { get; protected set; }
Вы можете вообще не определять setter, и возвращать какую-то высчитанную вами величину:
public int A
{
get { return b + c; }
}
(впрочем, такое можно сделать и при наличии сеттера). Например, вы можете предоставлят
данные в разных форматах:
public double Radians { get; set; }
public double Degrees
{
get { return Radians * 180.0 / Math.PI; }
set { Radians = value * Math.PI / 180.0; }
}
Вы можете установить брейкпойнт на запись или чтение свойства! Брейкпойнты на запис
или чтение данных в отладчике управляемого кода Visual Studio пока (по крайней мер
до текущей на данный момент версии Visual Studio 2017, версия 15.7) не поддерживаются.
Вы можете задать наличие свойства в интерфейсе, в отличие от поля:
interface ISupportsA
{
int A { get; }
}
class Data : ISupportsA
{
public int A { get; set; }
}
Вы можете объявить свойство виртуальным! То есть вы сможете переопределить поведени
свойства в классах-наследниках. Попробуйте-ка сделать такое с полем.
Свойства не хуже полей в том смысле, что вы можете заставить свойство работать так
будет это просто поле (public int A { get; set; }), но вы не сможете поле заставит
работать как свойство. То есть практически всегда лучше «наружу» выставлять свойство
а не поле.
XML-сериализация и WPF-овский Binding работает лишь со свойствами, но не с полями
Да, это можно считать ошибкой во фреймворке, но фактически это так.
Но не излишни ли свойства в языке? Кажется, что вместо свойства можно определит
просто две функции:
class Data
{
private int a;
public int GetA() { return a; }
public int SetA(int a) { this.a = a; }
}
Ответ на это таков.
Во-первых, одно свойство вместо двух функций представляет собой логическую группу
В хорошем языке вы говорите то, что думаете. На самом деле вы предоставляете пользовател
«переменную» A с дополнительной, часто невидимой снаружи семантикой. Значит, и выглядет
она должна как одна переменная, чтобы пользователи класса думали в тех же терминах
что и вы.
Во-вторых, это читаемость текста. Сравните код со свойствами:
player.car.speed++;
и без них:
getPlayer().getCar().setSpeed(getPlayer().getCar().getSpeed()+1);
Что легче воспринимается?
Справедливости ради, нужно отметить и недостатки свойств по сравнению с полями.
Свойства нельзя использовать как out/ref-параметр, поля можно.
Доступ к полям очень быстр, а вот доступ к свойствам может быть медленным, если ко
внутри геттера/сеттера медленный. Однако, медленный сеттер или (ещё хуже) геттер считаютс
порочной практикой, их рекомендуется избегать, чтобы не разрушать ментальную модел
«переменная с небольшим довеском».
Доступ к свойству может выбросить исключение или зависнуть, в то время как боле
простые поля ведут себя крайне просто. Конечно, правильно написанное свойство не буде
зависать, а исключения я бы порекомендовал выбрасывать только в случаях, когда пользовател
класса нарушил контракт на доступ к полю.
Ещё одно тонкое отличие свойства от поля состоит в том, что геттер возвращает ва
копию значения, в то время как при работе с полем вы получаете доступ непосредственн
к переменной. При работе со полями reference-типов (то есть, тип которых есть класс
практической разницы нет, так как работа с объектом по копии ссылки не отличается о
работы по оригиналу ссылки. Разница, однако, есть, когда поле является изменяемой структуро
(хотя, сами по себе изменяемые структуры — плохая идея). Пример случая, когда это важно
во фрагменте кода ниже.
Часто считают, что можно для начала объявить данные как поле, а потом, при необходимости
«превратить» его в свойство. Это лишь отчасти верно: при этом вы теряете бинарную совместимость
Код, который использовал ваш класс, должен быть перекомпилирован, так как на уровн
скомпилированного кода обращение к полю и к свойству — не одно и то же. Кроме того
смысл кода может поменяться, приводя к тонким ошибками. Пример из статьи по ссылке выше:
using System;
struct MutableStruct
{
public int Value { get; set; }
public void SetValue(int newValue) { Value = newValue; }
}
class MutableStructHolder
{
public MutableStruct Field;
public MutableStruct Property { get; set; }
}
class Test
{
static void Main(string[] args)
{
MutableStructHolder holder = new MutableStructHolder();
// Меняет значение holder.Field
holder.Field.SetValue(10);
// Получает *копию* holder.Property и изменяет её
holder.Property.SetValue(10);
Console.WriteLine(holder.Field.Value); // 10
Console.WriteLine(holder.Property.Value); // 0
}
}
Кстати, согласно Википедии, геттер и сеттер правильно называть акцессор и мутато
соответственно. Вы об этом знали? [Хотя, MSDN пишет просто «методы доступа».]
Ответ 2
Я думаю, что use-case'ов, в которых полезно делать именно поле, довольно таки много
Попробую привести вам один пример, поясняющий мотивацию использования свойства вместо поля.
Представим, что вы разрабатываете библиотеку классов. И реализовали класс как-то так:
class SuperClass
{
public int a; // То есть, решили, что поля достаточно.
}
Вашей библиотекой пользуются другие разработчики (то есть, клиенты по отношению
вашему коду). И вот оказывается, что вы хотите добавить некую логику чтения или запис
a (например, считывание значения из конфига). Полем здесь не обойдешься - делаем свойство:
class SuperClass
{
public int a { get { /*какая-то логика*/ } set { /*какая-то логика*/ } }
}
Вы поставили библиотеку клиентам и оп-па... Их код больше не работает с вашим. Он
не могут просто поставить обновление в виде вашей библиотеки, им придется пересобрат
свои приложения, потому что поля a больше нет. Есть свойство, но не поле - внешний интерфей
класса изменился.
Код постоянно меняется, везде потенциально может появиться новая логика. Описанног
неудобства не случилось бы, если бы вы сразу объявили свойство.
Общий смысл примерно таков: используйте свойства даже когда это просто запись и чтени
из поля, тогда внешний интерфейс класса не изменится, даже если вы измените логику работ
с этим полем.
Ответ 3
Использование свойства вместо поля является реализацией принципа инкапсуляции - одног
из принципов объектно ориентированого программирования.
Ответ 4
Плюсов очень много.
Но важен тот, который приносит пользу в нужный момент.
Эта вещь мне принесла пользу.
Был класс, ну допустим с A. Этот класс использовали из кучи мест. Даже не знаеш
из каких. А обновить надо сейчас, через 15 минут. Поиск использования показывается 110
позиций, а надо поменять только там, где в него что-то пишем.
Делаем из public int A; ->
public int A {get; private set;}
И любое присвоение выпадет списком ошибок.
Ответ 5
В дополнение к подробнейшему ответу @VladD
Еще удобно инициализировать:
class Filter {
public int ID { get; set;}
public string Name { get; set;}
}
Filter filter = new Filter { ID = 15, Name = "Vasya" };
Ответ 6
смысл есть, если связываешь скрипты к примеру или хочешь видеть значение в инспекторе
если приват-не видно паблик-видно
Комментариев нет:
Отправить комментарий