This article has been localized into Russian by the community.
Свойства
В предыдущей статье мы обсуждали поля. Они похожи на глобальные переменные для класса, позволяя получить к ним доступ из всех методов. Мы также кратко обсудили тот факт, что поля могут быть доступны из других классов, если они помечены как public, но это, как правило, не рекомендуется. Для переменных/полей, к которым вы хотите получить доступ извне вашего класса, вместо этого вы должны использовать свойства.
Когда вы помечаете поле как public, вы предоставляете полный доступ извне - другие классы могут делать с ним все, что захотят, без какого-либо уведомления декларирующего класса. Свойства возвращают управление декларирующему классу, определяя, является ли поле доступным только для чтения или только для записи, и даже позволяя декларирующему классу проверять и манипулировать значением перед возвратом или назначением его полю.
Свойство выглядит немного как пересечение между полем и методом, потому что оно объявлено во многом как поле с видимостью, типом данных и именем, но оно также имеет тело, подобное методу, для управления поведением:
public string Name
{
get { return _name; }
set { _name = value; }
}
Обратите внимание на специальные ключевые слова get и set. Они используются исключительно для свойств, для управления поведением при чтении (get 'ing) и записи (set' ing) поля. Для создания свойств, доступных только для чтения или только для записи, можно также управлять видимостью реализаций get или set, например, создать свойство, которое может быть прочитано из любого места (public), но изменено только изнутри объявляющего класса (private).
Вы также заметите, что я ссылаюсь на поле с именем _name. Вы должны будете также объявить это в своем классе, чтобы ваше свойство могло использовать его. Общий шаблон использования полей и свойств будет выглядеть следующим образом:
private string _name = "John Doe";
public string Name
{
get { return _name; }
set { _name = value; }
}
Теперь можно увидеть, как поле и свойство работают вместе: метод get возвращает значение поля _name, а метод set присваивает переданное значение полю _name. В методе set используется специальное значение ключевого слова, которое в данной конкретной ситуации будет ссылаться на значение, переданное свойству.
Таким образом, это в значительной степени так просто, как получается, и на данный момент мы не делаем ничего, что не могло бы быть достигнуто с помощью простого публичного поля. Но позже вы можете решить, что хотите получить больший контроль над тем, как другие классы могут работать с именем, и поскольку вы реализовали это как свойство, вы можете изменять реализацию, не мешая никому использовать ваш класс. Например, свойство Name можно изменить, чтобы оно выглядело так:
private string _name = "John Doe";
public string Name
{
get
{
return _name.ToUpper();
}
set
{
if(!value.Contains(" "))
throw new Exception("Please specify both first and last name!");
_name = value;
}
}
Теперь метод get обеспечивает, чтобы возвращаемое значение всегда находилось в UPPERCASE, независимо от того, в каком случае используется поле поддержки (_name). В методе set мы добавили пара строк кода, чтобы проверить, содержит ли переданное значение пробел, потому что мы решили, что имя всегда должно состоять из имени и фамилии - если это не так, выдается исключение. Все это очень грубо и упрощенно, но оно должно иллюстрировать полный уровень контроля, который вы получаете при использовании свойств.
Свойства только для чтения
Большинство свойств, которые вы встретите в примерах данного руководства, будут доступны как для чтения {get;} так и для записи {set;}. Такое определение свойств наиболее распространено, однако делать так совершенно не обязательно. К примеру вы можете объявить свойство только для чтения:
private string _name = "John Doe";
public string Name
{
get { return _name; }
}
В этом случае вы больше не сможете изменить свойство "Name" - вы можете только прочитать его, и компилятор выдаст ошибку, если вы попытаетесь присвоить ему значение. Но вы все еще можете изменить его значение внутри класса, поскольку вы можете просто присвоить новое значение полю поддержки "_name". Однако такой подход лишает свойства одного из самых больших преимуществ: возможность всегда контролировать, может ли значение быть принято. Как мы уже говорили, метод set - это отличный способ проверки значения, но если вы присваиваете новое значение полю _name из нескольких источников, то, поскольку свойство доступно только для чтения, вы не получите такой проверки.
К счастью для нас, C# предлагает решение этой проблемы: вы можете задать для свойства метод set, но ограничить его видимость, воспользовавшись ключевым словом private или protected. Это даст вам оба преимущества: вы сможете присвоить свойству значение изнутри класса (или любого наследуемого класса, если вы используете ключевое слово protected) и пройти соответствующую проверку. Вот пример:
private string _name = "John Doe";
public string Name
{
get { return _name; }
private set
{
if(IsValidName(value))
this._name = value;
}
}
public bool IsValidName(string name)
{
return name.EndsWith("Doe");
}
Ключевым отличием здесь является просто ключевое слово "private" прямо перед ключевым словом "set", и, как уже упоминалось, вы можете заменить его, например, на protected или internal, в зависимости от ваших потребностей.
Автоматически реализованные свойства
В некоторых случаях вам не требуется полный контроль над полем, и может быть неудобно реализовывать как поле, так и свойство с помощью методов get и set, которые ничего не делают, кроме того, что мы видели в первом примере. У вас может возникнуть соблазн просто объявить вашу переменную как открытое поле, чтобы избежать всех этих дополнительных хлопот. Но не делай этого! К счастью для всех нас, Microsoft решила добавить автоматически внедряемые свойства в C # версии 3, что сэкономит вам несколько строк кода. Просто учтите разницу:
Обычное свойство с объявленным вспомогательным полем:
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
Точно такое же поведение, но с автоматически реализованным свойством:
public string Name { get; set; }
Обратите внимание, что методы get и set являются пустыми и что никакое частное вспомогательное поле не объявлено - другими словами, теперь мы можем выполнить то же поведение, что и в первом примере, но с помощью одной строки кода! Имейте в виду, что частное вспомогательное поле все еще будет существовать во время выполнения - оно будет автоматически реализовано компилятором, как следует из названия. Если впоследствии вы решите, что вам нужно больше контроля над этим конкретным свойством, вы можете просто изменить его на обычную комбинацию полей/свойств с желаемой реализацией методов get и set.
Обратите внимание, что при использовании автоматически реализуемых свойств у вас все еще остается важный механизм управления обычными свойствами: вы можете опустить ключевое слово set, чтобы создать свойство только для чтения, например, нравится:
public string ReadOnlyProperty { get; }
Свойства только для записи недопустимы при использовании автоматически реализуемых свойств.
Автоматически реализованные свойства со значениями по умолчанию
До C# версии 6 вы не могли определить значение по умолчанию для автоматически реализуемого свойства - для этого вам понадобится объявленное вспомогательное поле, которое позволит вам инициализировать переменную значением:
private string _name = "John Doe";
public string Name
{
get { return _name; }
set { _name = value; }
}
Но в C # версии 6 Microsoft наконец-то добавила возможность инициализировать автоматически реализованное свойство значением по умолчанию, например:
public string Name { get; set; } = "John Doe";
Свойства, связанные с выражением
Еще одна функция, связанная со свойствами, реализованная Microsoft в C# 6.0 и 7.0, - это элементы с телом выражения. Он просто позволяет вам писать однострочные выражения для ваших свойств и методов - в этом случае давайте посмотрим, как использовать его для ваших методов get / set таким образом, чтобы он занимал меньше места и требовал немного меньше ввода:
private string name;
public string Name
{
get => name;
set => name = value;
}
Если ваше свойство доступно только для чтения, синтаксис может быть еще короче:
public string Name => "John Doe";
Конечно, это также работает, если вам действительно нужно что-то сделать, прежде чем возвращать значение, например:
public string Name { get; set; } = "John Doe";
public string FirstName => this.Name.Substring(0, this.Name.IndexOf(" "));
Как вы можете видеть, это позволяет вам определить метод get, но без фактических ключевых слов get и return, поощряя вас сохранять все это в одной строке вместо нескольких строк.
Резюме
Свойства дают вашим классам больший контроль над тем, как к полям можно обращаться и манипулировать ими, и их всегда следует использовать, когда вы хотите предоставить доступ к полям извне объявленного класса.