Автоматически реализованные геттеры и сеттеры против общедоступных полей

Я вижу много примеров кода для классов C #, которые делают это:

public class Point {
    public int x { get; set; }
    public int y { get; set; }
}

Или, в более старом коде, то же самое с явным частным резервным значением и без новых автоматически реализуемых свойств:

public class Point {
    private int _x;
    private int _y;

    public int x {
        get { return _x; }
        set { _x = value; }
    }

    public int y {
        get { return _y; }
        set { _y = value; }
    }
}

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

public class Point {
    public int x;
    public int y;
}

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


person tclem    schedule 21.09.2008    source источник


Ответы (17)


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

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

person Dexter    schedule 21.09.2008
comment
Спасибо за эту ссылку и за то, что убедили меня, что при переключении поля / свойства что-то действительно сломается. Мне это странно, так как синтаксис все равно один и тот же. - person tclem; 21.09.2008
comment
Вы могли подумать, что все сводится к одному и тому же, но очевидно, что внутренняя механика немного отличается. - person Dexter; 21.09.2008
comment
В C # свойство с get и set компилируется в пару методов, таких как get_PropertyName () и set_PropertyName (значение типа). Синтаксис свойств в C # - это просто синтаксический сахар. Свойства могут выполнять код, поэтому для них не имело бы смысла компилировать до того же кода, что и для поля. - person Jason Jackson; 21.09.2008
comment
Компиляторы JIT оптимизируют свойство без кода, чтобы оно было эквивалентно полю, поэтому нет причин не использовать свойства с учетом сокращенного синтаксиса в C # 3.0+. - person Erv Walter; 03.06.2010

Также намного проще изменить его на это позже:

public int x { get; private set; }
person Brad Wilson    schedule 21.09.2008
comment
Я ... даже не знал, что ты можешь это сделать. - person Merus; 22.09.2008
comment
Правда, это очень полезно, к сожалению, в интерфейсе этого сделать нельзя. - person Georges D; 06.05.2019

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

person Joseph Daigle    schedule 21.09.2008
comment
Но как это меняет контракт класса? В любом случае point.x = 1; и point.x работают нормально. - person Ajedi32; 11.01.2017
comment
@ Ajedi32 технически это не один и тот же контракт. Если вы измените его с поля на свойство (или свойство на поле), вам придется перекомпилировать все, что обращается к этому члену. Поскольку скомпилированный IL отличается при доступе к свойству и полю. - person Joseph Daigle; 12.01.2017

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

C # может по-разному обрабатывать свойства и переменные. Например, вы не можете передавать свойства как параметры ref или out. Поэтому, если вам по какой-то причине нужно изменить структуру данных, и вы использовали общедоступные переменные, и теперь вам нужно использовать свойства, ваш интерфейс должен будет измениться, и теперь код, который обращается к свойству x, может больше не компилироваться, как когда он был переменным. Икс:

Point pt = new Point();
if(Int32.TryParse(userInput, out pt.x))
{
     Console.WriteLine("x = {0}", pt.x);
     Console.WriteLine("x must be a public variable! Otherwise, this won't compile.");
}

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

person Rob Pilkington    schedule 21.09.2008

Setter и Getter позволяют добавлять дополнительный уровень абстракции, и в чистом ООП вы всегда должны получать доступ к объектам через интерфейс, который они предоставляют внешнему миру ...

Рассмотрим этот код, который спасет вас в asp.net и который был бы невозможен без уровня абстракции, обеспечиваемого сеттерами и геттерами:

class SomeControl
{

private string _SomeProperty  ;
public string SomeProperty 
{
  if ( _SomeProperty == null ) 
   return (string)Session [ "SomeProperty" ] ;
 else 
   return _SomeProperty ; 
}
}
person Yordan Georgiev    schedule 22.04.2009

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

Например:

public string x { get; set; }

и, например, вы уже много раз используете x и не хотите нарушать свой код.

Как изменить установщик автоматического получения ... например, для установщика разрешается устанавливать только допустимый формат телефонного номера ... как изменить код, чтобы изменить только класс?

Моя идея - добавить новую частную переменную и добавить те же средства получения и установки x.

private string _x;

public string x { 
    get {return _x}; 
    set {
        if (Datetime.TryParse(value)) {
            _x = value;
        }
    }; 
}

Это то, что вы имеете в виду, говоря о гибкости?

person Nap    schedule 08.09.2009

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

person TheZenker    schedule 21.09.2008

Кроме того, вы можете ставить точки останова на геттеры и сеттеры, но не на поля.

person NotDan    schedule 08.09.2009

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

person Community    schedule 21.09.2008

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

С уважением

person marcospereira    schedule 21.09.2008

Также стоит отметить, что вы не можете сделать автоматические свойства только для чтения и не можете инициализировать их встроенными. Обе эти вещи я хотел бы увидеть в будущих версиях .NET, но я считаю, что вы не сможете сделать ни то, ни другое в .NET 4.0.

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

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

person MrLane    schedule 08.09.2009
comment
Это не совсем так, у вас могут быть автоматически реализованные свойства только для чтения (или только для записи), если у вас есть SomePropName {get; private set;}, а затем установить свойство с помощью инициализаторов объекта .. - person jhexp; 16.08.2010

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

person EricSchaefer    schedule 21.09.2008

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

person gizmo    schedule 21.09.2008

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

person Kibbee    schedule 21.09.2008

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

  1. Свойство - это член, который обеспечивает гибкий механизм только для чтения или только для записи.
  2. Свойства можно переопределить, а поля нельзя.
person jihed    schedule 19.11.2019

Добавление получателя и сеттера делает переменную свойством, как при работе в Wpf / C #.

Если это просто общедоступная переменная-член, она недоступна из XAML, потому что это не свойство (даже если его общедоступная переменная-член).

Если у него есть сеттер и получатель, то он доступен из XAML, потому что теперь это свойство.

person zar    schedule 18.01.2020

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

Нет, технически разницы нет, и когда я действительно хочу предоставить общий доступ к объекту в наши дни, я иногда делаю его общедоступным final вместо добавления геттера.

Способ «продажи» сеттеров и получателей состоит в том, что вам может потребоваться знать, что кто-то получает значение или изменяет его, что имеет смысл только с примитивами.

Объекты пакета свойств, такие как DAO, DTO и экранные объекты, исключаются из этого правила, потому что они не являются объектами в реальном «OO Design» значении слова Object. (Вы не думаете о «передаче сообщений» в DAO, это просто куча пар атрибут / значение).

person Bill K    schedule 23.09.2008