Зачем вообще использовать поля вместо свойств?

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

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

Изменить: некоторые люди подняли вопрос о необходимости резервного копирования свойств с полями (явно или автоматически). Позвольте прояснить мой вопрос: есть ли причина использовать поля кроме резервного копирования свойств? То есть, когда-нибудь SomeType someField; предпочтительнее SomeType SomeProperty { get; set; }?

Изменить 2: ДанМ, Скурмедель и Сет дали действительно полезные ответы. Я принял DanM, так как он является наиболее полным, но если бы кто-то суммировал свои ответы в один ответ, я был бы счастлив принять его.


person Matthew    schedule 30.01.2010    source источник
comment
дубликат stackoverflow.com/questions/295104/   -  person user73993    schedule 30.01.2010
comment
Нет, я думаю, что это отличный вопрос, к тому же очень тонкий. Однозначно другое.   -  person Mark Byers    schedule 30.01.2010
comment
Это не дубликат. Другой вопрос: в чем разница между A и B; этот признает разницу между A и B и спрашивает, делает ли A B устаревшим?   -  person jyoungdev    schedule 07.06.2010
comment
В C # 6.0 автоматические свойства будут поддерживать встроенную инициализацию и ключевое слово readonly, что делает этот вопрос еще более актуальным.   -  person hypehuman    schedule 11.12.2015
comment
@hypehuman Я знаю встроенную инициализацию SomeType SomeProperty { get; } = new SomeType();, но что вы имеете в виду под ключевым словом readonly?   -  person Sebastian Werk    schedule 27.06.2018
comment
@SebastianWerk Упс, такого нет. Я, должно быть, был сбит с толку тогда, когда прочитал объявление. Свойства, доступные только для чтения, неявно определяются отсутствием установщика; они не используют явное ключевое слово readonly.   -  person hypehuman    schedule 30.06.2018


Ответы (13)


Обычно свойствам требуется вспомогательное поле, если они не являются простыми «автоматическими свойствами» получателя / установщика.

Итак, если вы просто делаете ...

public string Name { get; set; } // automatic property

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

Однако, если вы делаете ...

public string Name
{
    get { return _name; }
    set 
    {
       if (value = _name) return;
       _name = value;
       OnPropertyChange("Name");
    }
}

... вам нужно это _name вспомогательное поле.

Для частных переменных, которые не требуют какой-либо специальной логики получения / установки, это на самом деле вызов суждения, делать ли частное автоматическое свойство или просто поле. Обычно я делаю поле, а затем, если мне нужно, чтобы оно было protected или public, я изменяю его на автоматическое свойство.

Обновить

Как заметил Ясир, если вы используете автоматические свойства, за кадром все еще скрывается поле, просто это не то, что вам действительно нужно печатать. Итак, суть в следующем: свойства не хранят данные, они предоставляют доступ к данным. Поля - это то, что на самом деле хранит данные. Значит, они вам нужны, даже если вы их не видите.

Обновление 2

Что касается вашего измененного вопроса ...

бывает ли когда-нибудь SomeType someField; предпочтительнее SomeType SomeProperty { get; set; }?

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

Еще одна вещь, менее важная, - это то, что public string Name { get; set; } требует большего набора текста (и немного сложнее), чем private string _name.

person devuxer    schedule 30.01.2010
comment
Следует отметить, что компилятор создает поле для каждого свойства auto. - person Hannoun Yassir; 30.01.2010
comment
Я думаю, что главная идея плаката такова: даже ваше поле _name в приведенном вами примере может быть частным автоматически реализуемым свойством. Теоретически вам вообще не нужно знать о полях, это просто детали реализации того, как работают свойства, они могут быть полностью скрыты от разработчика компилятором в качестве деталей реализации, как и регистры ЦП. - person Mark Byers; 30.01.2010
comment
@Mark, вы технически правы, но использование частной собственности в качестве поддерживающего свойства может нарушить соглашение, что сделало бы намерение вашего кода неясным для тех, кто должен его читать или поддерживать. Это также создает проблему с именованием. Если публичная собственность Name, что бы вы назвали частной собственностью? Если вы назовете это _name или _Name, другие люди, читающие ваш код, скорее всего, сделают неверные выводы о том, является ли эта переменная свойством или полем. Тем не менее, возможно, однажды различие между свойствами и полями исчезнет. - person devuxer; 30.01.2010
comment
@DanM: Спасибо за ответ. На самом деле я понимаю разницу между SomeType someField; и SomeType SomeProperty { get; set; }. За кулисами SomeProperty могут иметь автоматически реализованные поля, но с точки зрения программиста, есть ли причина предпочесть someField? Я отредактировал свой исходный вопрос, чтобы прояснить это. - person Matthew; 30.01.2010
comment
@Matthew, я обновил свой ответ, чтобы обновить ваш вопрос. - person devuxer; 30.01.2010
comment
@Dan: Тем не менее, может быть, однажды разница между свойствами и полями исчезнет. Я тоже так думаю. Но, вероятно, на это потребуются поколения. Мы только недавно отучились от ‹s› ассемблера ‹/s› C ++. - person Mark Byers; 30.01.2010
comment
@Dan: (в соответствии с соглашением о свойствах) я думаю, что соглашение об именах больше связано с тем, является ли поле / свойство общедоступным, чем с тем, является ли оно свойством. Если бы у вас были общедоступные поля, в них, вероятно, тоже была бы заглавная буква, а в частных свойствах - нет. - person Mark Byers; 30.01.2010
comment
@Mark, я согласен, что публичное и приватное - это важное различие, но если вы начнете писать private string _name { get; set; }, люди будут смотреть на вас смешно (и у меня такое чувство, что StyleCop не обрадуется) :) - person devuxer; 30.01.2010
comment
Мне этого достаточно. Причины, по которым раскрытие свойств и сохранение переменных для личных целей (следуя соглашениям объектно-ориентированного программирования) ясны! Еще одно хорошее объяснение дает @BillW. - person Erup; 11.05.2010

Просто попробуйте использовать свойство при использовании аргументов ref / out:

someObject.SomeMethod(ref otherObject.SomeProperty);

Он не компилируется.

person Jenk    schedule 01.04.2010

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

Предотвращение чрезмерного использования средств получения и установки собственности

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

Источник: http://dotnet.sys-con.com/node/46342

person Seth    schedule 30.01.2010
comment
Невиртуальные средства получения или установки свойств, которые не содержат никаких инструкций, кроме доступа к полю, будут встроены компилятором. Случай, на который он ссылается (частные автоматически реализованные свойства), попадет в эту категорию, поэтому это не повлияет на производительность. - person Mark Byers; 30.01.2010

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

Я делаю это довольно часто.

Надуманный пример:

class Rectangle
{
   private readonly int _width;
   private readonly int _height;

   public Rectangle(int width, int height)
   {
      _width = width;
      _height = height;
   }

   public int Width { get { return _width; } }
   public int Height { get { return _height; } }
}

Это означает, что ничто внутри Rectangle не может изменить ширину или высоту после построения. Если кто-то попытается, компилятор пожалуется.

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

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

person Skurmedel    schedule 30.01.2010
comment
Это полезно знать. Почему ты не можешь сделать public readonly int Width { get; set; }? - person Matthew; 30.01.2010
comment
Без понятия. Я могу только догадываться; возможно, это было сокращено в пользу других функций, или они не видели необходимости. Вы должны спросить об этом Эрика Липперта :) - person Skurmedel; 30.01.2010
comment
Поскольку вы можете возиться только с полями только для чтения в конструкторе (или при их объявлении), я не могу себе представить, как будет выглядеть хороший синтаксис для объявления свойства только для чтения. - person Skurmedel; 30.01.2010
comment
@Matthew: Почему вы не можете сделать public readonly int Width {get; набор; } Думаю, это еще один отличный вопрос. :) Хотелось бы узнать ответ и на это. - person Mark Byers; 30.01.2010
comment
@Mark: Вам повезло. stackoverflow.com/questions/2166744/ - person Matthew; 30.01.2010
comment
C # 6.0 теперь поддерживает свойства только для чтения! object MyProp { get; } Это свойство может быть установлено встроенным (object MyProp { get; } = ...) или в конструкторе, но нигде больше (как в полях только для чтения). - person Matthew; 14.11.2015
comment
С C # 9.0 это стало еще проще: public int Width { get; init; } - person Sedat Kapanoglu; 22.08.2020

Хотя я согласен с тем, что я воспринимаю как «намерение» в заявлении Дэвида Басараба: «Нет причин публично раскрывать поля», я хотел бы добавить немного другой акцент:

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

Свойства - это не просто «оболочка» синтаксиса над полями, «наклеенными на» C #: они являются фундаментальной функцией языка, разработанной по уважительным причинам, в том числе:

  1. контроль того, что отображается и не отображается вне классов (инкапсуляция, скрытие данных)

  2. разрешение выполнения определенных действий при доступе к свойству или его установке: действия, которые лучше всего выражаются в свойствах get and 'set, а не «повышаются» до методов, определенных извне.

  3. Интерфейсы по дизайну не могут определять поля: но могут определять свойства.

Хороший объектно-ориентированный дизайн означает осознанный выбор «состояния»:

  1. поля локальных переменных: какое состояние является частным для метода и переходным: локальные переменные, как правило, действительны только в рамках тела метода или даже со столь же "узким сроком службы", как в рамках чего-то вроде цикл for. Конечно, вы также можете рассматривать переменные параметров в методе как «локальные».

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

  3. статические поля экземпляра: какое состояние будет свойством только класса, независимо от количества экземпляров класса.

  4. состояние намеренно и сознательно выставлено «вне» класса: ключевая идея состоит в том, что существует по крайней мере один уровень косвенности, вставленный между классом и «потребителями» данных, которые класс предоставляет. «Обратная сторона» «раскрытия» - это, конечно, сознательное намерение скрыть (инкапсулировать, изолировать) код реализации.

    а. через общедоступные свойства: все аспекты этого хорошо освещены во всех других ответах здесь

    б. через индексаторы

    c. через методы

    d. общедоступные статические переменные обычно находятся в служебных классах, которые часто являются статическими.

Предлагаем вам обзор: MSDN на 'Полях .. . MSDN в свойствах ... MSDN в индексаторах

person BillW    schedule 30.01.2010

Я не понимаю, зачем вам использовать частные автосвойства. В чем преимущество

private int Count {get; set;}

над

private int count
person Community    schedule 30.01.2010
comment
Свойство по умолчанию можно сделать виртуальным, чтобы его можно было переопределить, и вы можете контролировать получение и установку доступа независимо (например, сделать доступный метод доступа общедоступным, а установленный метод доступа защищенным). Что делать, если они вам сначала не нужны, но могут понадобиться позже? Затем вы можете начать с поля и использовать свойство позже, если только вы не обращаетесь к полям или свойствам через отражение. Так что ... преимущество бывает только иногда. - person jyoungdev; 07.06.2010
comment
Стоит также отметить, что если вы позже конвертируете из поля в свойство из-за изменения требований, все, что ссылается на поле, также должно быть перекомпилировано. - person Brandon Barkley; 28.09.2015

Нет причин для публикации полей нет.

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

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

person David Basarab    schedule 30.01.2010
comment
Не только то, что публичное раскрытие полей противоречит парадигме ООП с точки зрения сокрытия информации внутри конкретного класса ... - person t0mm13b; 30.01.2010
comment
Я не думаю, что это действительно ответ на его вопрос. Он хочет знать, почему в определенных ситуациях поля предпочтительнее свойств. - person Mark Byers; 30.01.2010
comment
Утверждение, что нет причин для публичного раскрытия полей, неверно; в таком случае функция была бы незаконной. Есть ряд веских причин для публичного раскрытия поля. Например, создание изменяемой структуры для использования в сценарии взаимодействия с устаревшим COM-объектом. Это особенно уместно, когда поля объекта передаются как аргументы ref или out в устаревший метод COM-объекта. - person Eric Lippert; 30.01.2010
comment
Я поддерживаю использование полей при использовании ref или out. Требуется тонна дополнительного кода для настройки для переноса в то, что по сути является DTO. Поэтому я просто изменил свойства на поля. Зачем все усложнять? - person Nathan; 06.02.2011

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

Вы не всегда видите поля. С автоматическими свойствами C # 3 поле создается для вас компилятором. Но он все еще там. Кроме того, автоматические свойства имеют некоторые существенные ограничения (например, отсутствие поддержки INotifyPropertyChanged, отсутствие бизнес-логики в установщиках), что означает, что они часто неуместны, и вам в любом случае необходимо создать явное поле и свойство, определяемое вручную.

Согласно ответ Дэвида, вы правы, если вы говорите об API: вы почти никогда не хотите, чтобы внутреннее состояние (поля) было частью API.

person itowlson    schedule 30.01.2010

Синтаксис для полей писать намного быстрее, чем для свойств, поэтому, когда безопасно использовать поле (частное для класса), почему бы не использовать его и не сохранить лишнюю типизацию? Если бы автоматически реализованные свойства имели приятный короткий сжатый синтаксис и вам приходилось проделывать дополнительную работу, чтобы создать простое старое поле, люди могли бы просто вместо этого начать использовать свойства. Кроме того, теперь это соглашение на C #. Так думают люди, и это то, что они ожидают увидеть в коде. Если вы сделаете что-то отличное от обычного, вы запутаете всех.

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

Есть очень простая причина, по которой нам все еще нужны явные поля:

В C # 1.0 не было всех этих хороших функций, которые есть сейчас, поэтому поля были фактом жизни - без них невозможно жить. Очень много кода полагается на то, что поля и свойства явно разные. Сейчас его просто невозможно изменить, не взломав тонны кода.

Я также подозреваю, что это влияет на производительность, но, возможно, это можно решить с помощью джиттера.

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

person Mark Byers    schedule 30.01.2010
comment
Зачем вам все время иметь недвижимость ?! Или вы имеете в виду модификатор в поле, который сказал ему создать свойство (как бы это ни выглядело;))? - person Skurmedel; 30.01.2010
comment
Почему вы хотите использовать два разных синтаксиса для этого члена? ;-) - person Mark Byers; 30.01.2010
comment
На самом деле вам не понадобился бы модификатор, если бы все было свойством. Вам даже не нужно было бы говорить public int Foo { get; set; }. Было бы хорошо просто сказать public int Foo;, потому что, если все является свойством, это будет безопасно, а код более лаконичен. Но очевидно, что в C # этого никогда не случится. Решение иметь две разные вещи, «поля» и «свойства», высечено в камне и не может быть изменено. - person Mark Byers; 30.01.2010
comment
Я не совсем понимаю, как это могло бы позволить ленивую загрузку или проверку? - person Skurmedel; 30.01.2010
comment
@Skurmedel: Я не говорю, что нужно отказываться от синтаксиса свойств, я говорю, что если синтаксис, используемый в настоящее время для поля, на самом деле означал автоматически реализуемое свойство, то поля вам вообще не понадобятся. Я также говорю, что вносить это изменение совершенно непрактично, по крайней мере, для C #. :) - person Mark Byers; 30.01.2010
comment
Нет, я понимаю, что вы имеете в виду, но где бы вы хранили вещи, если бы вам нужно было провести некоторую проверку или что-то подобное, в другом объекте? - person Skurmedel; 30.01.2010
comment
@Skurmedel: Да, это был бы тот же самый код, который вы пишете сейчас, только поле поддержки будет фактически свойством поддержки! Не забыл ли я упомянуть, что использование свойств, в которых люди ожидают увидеть поля, всех запутает? Хммм ... Ага, я сделал! ;) - person Mark Byers; 30.01.2010
comment
Хе-хе, хорошо, я понимаю, что ты имеешь в виду :) - person Skurmedel; 30.01.2010

Поля и свойства не взаимозаменяемы. Я предполагаю, что вы имеете в виду доступ к частным полям через частные свойства. Я делаю это, когда это имеет смысл, но по большей части в этом нет необходимости. Оптимизатор JIT в любом случае будет встраивать доступ к частному полю через частное свойство. И перенос частного поля в частное свойство в любом случае не считается критическим изменением, поскольку частные члены не являются частью вашего интерфейса.

Лично я бы никогда не раскрывал какие-либо защищенные / общедоступные поля экземпляра. Тем не менее, как правило, допустимо предоставлять общедоступное статическое поле с модификатором readonly, если тип поля сам по себе является неизменяемым. Это часто наблюдается со статическими полями SomeStruct.Empty.

person Josh    schedule 30.01.2010

Как отмечали другие, вам в любом случае понадобится частное резервное поле для свойств.

Также есть преимущество в скорости использования полей над свойствами. В 99,99% случаев это не имеет значения. Но в некоторых может.

person Joey    schedule 30.01.2010

Скорость. Если поле устанавливается или считывается миллиарды раз в ходе моделирования, тогда вы хотите использовать поле, а не свойство, чтобы избежать накладных расходов или вызова подпрограммы. В соответствии с OO (DDD?), Насколько это возможно, в этих случаях я бы рекомендовал прибегать к полям только в классе, предназначенном для представления какой-то «ценности», такой как человек. Логика должна быть сведена к минимуму. Лучше иметь человека-создателя или человека-обслуживающего персонала.

Но если у вас есть эти проблемы, то вы, вероятно, не программируете на C ++ и не на C #, не так ли?

person Martin    schedule 30.01.2010

Есть несколько хороших (частичных) ответов от @Seth (поля работают лучше, поэтому в частном контексте вы также можете использовать это в своих интересах, когда это имеет смысл), @Skurmedel (поля могут быть доступны только для чтения), @Jenk (поля могут быть использоваться для ссылки / выхода). Но хочу добавить еще одно:

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

private int x = 7;

vs

private int x { get; set; }

// This must go in the constructor, sometimes forcing you to create 
// a constructor that has no other purpose.
x = 7;
person Edward Ned Harvey    schedule 12.03.2015
comment
Теперь это возможно с C # 6 - person Gaspa79; 03.10.2016
comment
Теперь вы можете делать private int x { get; set; } = 7;. Я думаю, это то, что говорит Гаспа. - person BlueStaggo; 31.05.2021