Открытые поля и автоматические свойства


353

Нам часто говорят, что мы должны защищать инкапсуляцию, создавая методы getter и setter (свойства в C #) для полей класса, вместо того, чтобы открывать поля внешнему миру.

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

public class Book
{
    private string _title;

    public string Title
    {
          get{ return _title;  }
          set{ _title = value; }
    }
}

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

Затем идет C # 3.0, и я вижу, что они добавили автоматические свойства:

public class Book
{
    public string Title {get; set;} 
}

что является более аккуратным, и я благодарен за это, но на самом деле, что же отличается от простого публичного выступления?

public class Book
{
    public string Title;
}


6
Я преобразовал поле в свойство, чтобы установить точку останова на установщике
Ian Ringrose

1
Я склонен делать что-то, что не является частной собственностью, потому что понимание того, что я должен преобразовать поле в собственность, привело к ненужной головной боли. Свойства, поля и методы. Боже мой! Вызывает несовместимость, которая укусила меня в прошлом.
Стивен Векслер

2
propФрагмент кода позволяет быстро создавать свойства. Просто введите propвкладку.
Тоно Нам

Ответы:


175

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

Свойства против публичных переменных

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

    TryGetTitle(out book.Title); // requires a variable

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

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

28
Также поле является переменной и может быть передано по ссылке ( refили outключевому слову), а свойство является парой методов доступа и не может быть передано по ссылке. Например, bool success = TryGetMyTitle(out myBook.Title);который использует, outбудет работать с полем, а не работать со свойством. Это четкий пример того, почему переход от поля к свойству является серьезным изменением!
Джепп Стиг Нильсен

2
@KyleBaran Нет, в этом нет особого смысла, потому что свойство - это пара методов доступа, а не переменная. Обычно нужно объявить локальную переменную (возможно, прочитать свойство и поместить его значение в локальную переменную), передать локальную переменную как ref/ out, а затем установить для свойства значение, которое имеет локальная переменная. Но тогда вызываемый метод сам по себе не обращается к свойству, он обращается к локальной переменной, которую вы там сделали.
Джеппе Стиг Нильсен

5
@theberserker Правда, хотя в C # 6 вы можете сделать, public int Foo { get; }что создаст авто-свойство с полем только для чтения.
Майкл Стум

83

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

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

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


2
Десять лет спустя, точки останова данных здесь, по крайней мере для .NET Core :)
Luaan

72

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

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


9
Мне понравился этот ответ, потому что он не использует слова «отражение», «интерфейс» или «переопределить». (слишком плохо о 'контракте')

65

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


1
Я бы сказал, что если вам нужен интерфейс, который определяет свойства, это должен быть абстрактный класс. То, что c # позволяет вам определять свойства в интерфейсах, не означает, что вы должны их использовать. Это плохой дизайн.
Одис

8
@odyodyodys - я не уверен, что согласен, что это плохой дизайн. Пожалуйста, объясните свое обоснование?
Заид Масуд

4
@odyodyodys Я согласен с zooone9243: Imp, с точки зрения дизайна, нет никакой разницы между объявлением свойства и объявлением пары геттер / сеттер (что является обычной практикой для интерфейсов).
MartinStettner

22
@ zooone9243, + MartinStettner: это было 6 месяцев назад, с тех пор я многому научился. Я забираю его обратно
Одис

47

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


10

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


10

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


8

Нет ничего плохого в создании поля public. Но помните, что создание getter/setterс privateполями - это не инкапсуляция. ИМО, если вам не нужны другие функции Property, вы можете сделать это public.


1

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

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

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


0

Одна вещь, которую я нахожу очень полезной, так же как и весь код и причины тестирования, состоит в том, что, если это свойство по сравнению с полем, то в среде IDE Visual Studio отображаются ссылки на свойство, а не поле.


0

Мой POV после некоторых исследований

  1. Проверка.
  2. Разрешить переопределение метода доступа для изменения поведения свойства.
  3. Цель отладки. Мы сможем узнать, когда и что изменится свойство, установив точку останова в методе доступа.
  4. Мы можем иметь только набор полей. Например, public set () и private get (). Это невозможно в публичном поле.

Это действительно дает нам больше возможностей и расширяемости.

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