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

Я вижу много примеров кода для классов 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;
}

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
78
0
34 768
17
Перейти к ответу Данный вопрос помечен как решенный

Ответы 17

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

Но как это меняет контракт класса? В любом случае point.x = 1; и point.x работают нормально.

Ajedi32 11.01.2017 21:30

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

Joseph Daigle 12.01.2017 19:33

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

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

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

public int x { get; private set; }

Я ... даже не знал, что ты можешь это сделать.

Merus 22.09.2008 04:16

Правда, это очень полезно, к сожалению, в интерфейсе этого сделать нельзя.

Georges 06.05.2019 03:25
Ответ принят как подходящий

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

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

Спасибо за эту ссылку и за то, что убедили меня, что при переключении поля / свойства что-то действительно сломается. Мне это странно, так как синтаксис все равно один и тот же.

tclem 21.09.2008 21:31

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

Dexter 21.09.2008 21:34

В C# свойство с get и set компилируется в пару методов, таких как get_PropertyName () и set_PropertyName (значение типа). Синтаксис свойств в C# - это просто синтаксический сахар. Свойства могут выполнять код, поэтому для них не имело бы смысла компилировать до того же кода, что и для поля.

Jason Jackson 21.09.2008 21:54

Компиляторы JIT оптимизируют свойство без кода, чтобы оно было эквивалентно полю, поэтому нет причин не использовать свойства с учетом сокращенного синтаксиса в C# 3.0+.

Erv Walter 03.06.2010 06:52

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

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

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.");
}

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

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

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

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

С уважением

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

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

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

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

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

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

class SomeControl
{

private string _SomeProperty  ;
public string SomeProperty 
{
  if ( _SomeProperty == null ) 
   return (string)Session [ "SomeProperty" ] ;
 else 
   return _SomeProperty ; 
}
}

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

Например:

public string x { get; set; }

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

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

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

private string _x;

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

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

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

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

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

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

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

jhexp 16.08.2010 17:42

why we dont just use public fields instead of using properties then call accessors ( get,set ) when we dont need to make validations ?

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

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

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

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

Другие вопросы по теме