Разница между Собственностью и Полем в C# 3.0+
Я понимаю, что это, кажется, дубликат В чем разница между полем и свойством в C#? но мой вопрос имеет небольшую разницу (с моей точки зрения):
Однажды я знаю, что
- Я не буду использовать свой класс с "методами, которые работают только на свойства" и
- Я не буду использовать проверочный код в получателе / установщике.
Есть ли какая-либо разница (кроме стиля / будущего развития), например, какой-то тип управления в настройке свойства?
Есть ли дополнительная разница между:
public string MyString { get; set; }
а также
public string myString;
(Мне известно, что для первой версии требуется C# 3.0 или выше и что компилятор создает закрытые поля.)
10 ответов
Инкапсуляция.
Во втором случае вы только что определили переменную, во-первых, вокруг переменной есть метод получения / установки. Так что, если вы решите проверить переменную позднее, это будет намного проще.
Кроме того, они отображаются по-разному в Intellisense:)
Редактировать: Обновить для OPs обновленный вопрос - если вы хотите игнорировать другие предложения здесь, другая причина в том, что это просто не хороший дизайн OO. И если у вас нет веских причин для этого, всегда выбирайте свойство вместо публичной переменной / поля.
Поля и свойства выглядят одинаково, но это не так. Свойства - это методы, и как таковые существуют определенные вещи, которые не поддерживаются для свойств, и некоторые вещи, которые могут происходить со свойствами, но никогда в случае полей.
Вот список отличий:
- Поля могут быть использованы в качестве входных данных для
out/ref
аргументы. Свойства не могут. - Поле всегда будет давать один и тот же результат при вызове несколько раз (если мы пропустим проблемы с несколькими потоками). Свойство, такое как
DateTime.Now
не всегда равен себе. - Свойства могут выдавать исключения - поля никогда этого не сделают.
- Свойства могут иметь побочные эффекты или выполняться очень долго. Поля не имеют побочных эффектов и всегда будут такими быстрыми, как можно ожидать для данного типа.
- Свойства поддерживают различную доступность для получателей / установщиков - поля нет (но поля могут быть сделаны
readonly
) - При использовании отражения свойства и поля рассматриваются как разные
MemberTypes
поэтому они расположены по разному (GetFields
противGetProperties
например) - JIT-компилятор может трактовать доступ к свойству совсем иначе, чем доступ к полю. Тем не менее, он может компилироваться до идентичного собственного кода, но возможности для различия есть.
Пара быстрых, очевидных отличий
Свойство может иметь ключевые слова доступа.
public string MyString { get; private set; }
Свойство может быть переопределено в потомках.
public virtual string MyString { get; protected set; }
Принципиальное отличие состоит в том, что поле - это позиция в памяти, где хранятся данные указанного типа. Свойство представляет одну или две единицы кода, которые выполняются для получения или установки значения указанного типа. Использование этих методов доступа синтаксически скрыто за счет использования элемента, который, по-видимому, ведет себя как поле (в том смысле, что он может появляться с любой стороны операции присваивания).
Средства доступа - это больше, чем поля. Другие уже указали на несколько важных отличий, и я собираюсь добавить еще одно.
Свойства принимают участие в интерфейсных классах. Например:
interface IPerson
{
string FirstName { get; set; }
string LastName { get; set; }
}
Этот интерфейс может быть удовлетворен несколькими способами. Например:
class Person: IPerson
{
private string _name;
public string FirstName
{
get
{
return _name ?? string.Empty;
}
set
{
if (value == null)
throw new System.ArgumentNullException("value");
_name = value;
}
}
...
}
В этой реализации мы защищаем как Person
класс от попадания в недопустимое состояние, а также вызывающий от выхода из неназначенного свойства.
Но мы могли бы продвинуть дизайн еще дальше. Например, интерфейс может не работать с сеттером. Вполне законно сказать, что потребители IPerson
Интерфейс заинтересован только в получении свойства, а не в его настройке:
interface IPerson
{
string FirstName { get; }
string LastName { get; }
}
Предыдущая реализация Person
класс удовлетворяет этот интерфейс. Тот факт, что это позволяет вызывающей стороне также устанавливать свойства, не имеет смысла с точки зрения потребителей (которые потребляют IPerson
). Дополнительная функциональность конкретной реализации учитывается, например, застройщиком:
class PersonBuilder: IPersonBuilder
{
IPerson BuildPerson(IContext context)
{
Person person = new Person();
person.FirstName = context.GetFirstName();
person.LastName = context.GetLastName();
return person;
}
}
...
void Consumer(IPersonBuilder builder, IContext context)
{
IPerson person = builder.BuildPerson(context);
Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
}
В этом коде потребитель не знает об установщиках свойств - это не его дело знать об этом. Потребителю нужны только геттеры, и он получает геттеры из интерфейса, то есть из контракта.
Еще одна полностью действительная реализация IPerson
будет классом неизменного человека и соответствующим человеком фабрики:
class Person: IPerson
{
public Person(string firstName, string lastName)
{
if (string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName))
throw new System.ArgumentException();
this.FirstName = firstName;
this.LastName = lastName;
}
public string FirstName { get; private set; }
public string LastName { get; private set; }
}
...
class PersonFactory: IPersonFactory
{
public IPerson CreatePerson(string firstName, string lastName)
{
return new Person(firstName, lastName);
}
}
...
void Consumer(IPersonFactory factory)
{
IPerson person = factory.CreatePerson("John", "Doe");
Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
}
В этом примере кода потребитель еще раз не знает, как заполнить свойства. Потребитель имеет дело только с геттерами, а конкретная реализация (и бизнес-логика, стоящая за ним, например, тестирование, если имя пусто) оставлена на усмотрение специализированных классов - разработчиков и фабрик. Все эти операции совершенно невозможны с полями.
Первый:
public string MyString {get; set; }
является собственностью; второй (public string MyString
) обозначает поле.
Разница в том, что некоторые методы (привязка данных ASP.NET для экземпляров) работают только со свойствами, а не с полями. То же самое относится и к XML-сериализации: сериализуются только свойства, поля не сериализуются.
Свойства и поля во многих случаях могут показаться похожими, но это не так. Существуют ограничения для свойств, которые не существуют для полей, и наоборот.
Как уже упоминали другие. Вы можете сделать свойство доступным только для чтения или только для записи, сделав его метод доступа закрытым. Вы не можете сделать это с полем. Свойства также могут быть виртуальными, а поля - нет.
Думайте о свойствах как о синтаксическом сахаре для функций getXXX()/setXXX(). Вот как они реализуются за кулисами.
Среди других ответов и примеров, я думаю, этот пример полезен в некоторых ситуациях.
Например, допустим, у вас есть OnChange
property
как следующее:
public Action OnChange { get; set; }
Если вы хотите использовать делегатов, чем вам нужно изменить его OnChange
в field
как это:
public event Action OnChange = delegate {};
В такой ситуации мы защищаем наше поле от нежелательного доступа или модификации.
Есть еще одно важное различие между полями и свойствами.
При использовании WPF вы можете связывать только общедоступные свойства. Привязка к общедоступному полю не будет работать. Это верно, даже когда не реализует INotifyPropertyChanged
(хотя вы всегда должны).
Вы должны всегда использовать свойства вместо полей для любых открытых полей. Это гарантирует, что ваша библиотека сможет реализовать инкапсуляцию для любого поля, если это потребуется в будущем, без нарушения существующих кодов. Если вы замените поля свойствами в существующих библиотеках, то все зависимые модули, использующие вашу библиотеку, также должны быть перестроены.