Свойства (Руководство по программированию в C#)
Свойство — это элемент, предоставляющий гибкий механизм для чтения, записи или вычисления значения поля данных. Свойства отображаются как открытые элементы данных, но они реализуются как специальные методы, называемые методами доступа. Эта функция позволяет вызывающим пользователям легко получать доступ к данным и по-прежнему способствовать обеспечению безопасности и гибкости данных. Синтаксис свойств является естественным расширением полей. Поле определяет место хранения:
public class Person
{
public string? FirstName;
// Omitted for brevity.
}
Автоматически реализуемые свойства
Определение свойства содержит объявления для методов доступа get
и set
, которые получают и устанавливают значение этого свойства:
public class Person
{
public string? FirstName { get; set; }
// Omitted for brevity.
}
В предыдущем примере показано автоматическое свойство. Компилятор создает скрытое поле резервного копирования для свойства. Компилятор также реализует тело методов доступа get
и set
. Все атрибуты применяются к свойству автоматической реализации. Атрибут можно применить к поле резервной копии, созданному компилятором, указав field:
тег атрибута.
Можно инициализировать свойство в значение, отличное от значения по умолчанию, задав значение после закрывающей фигурной скобки для свойства. Вы можете предпочесть начальное значение для FirstName
свойства пустой строкой, а не null
. Можно указать, что, как показано в следующем коде:
public class Person
{
public string FirstName { get; set; } = string.Empty;
// Omitted for brevity.
}
Управление доступом
В предыдущих примерах показаны свойства чтения и записи. Вы также можете создать свойства только для чтения или предоставить различные специальные возможности набору и получить методы доступа. Предположим, ваш класс Person
должен допускать изменение значения свойства FirstName
только из других методов этого класса. Вы можете предоставить методу доступа set уровень доступа private
, а не public
:
public class Person
{
public string? FirstName { get; private set; }
// Omitted for brevity.
}
Свойство FirstName
можно считывать из любого кода, но его можно назначить только из кода в Person
классе.
Вы можете добавить любой ограничивающий модификатор доступа для методов доступа set или get. Модификатор доступа для отдельного метода доступа должен быть более строгим, чем доступ к свойству. Предыдущий код является законным, так как FirstName
свойство имеет public
значение, но метод доступа set имеет значение private
. Невозможно объявить private
свойство с методом public
доступа. Свойство также можно объявить как protected
, internal
, protected internal
или даже private
.
Существует два специальных модификатора доступа для set
методов доступа:
- Метод
set
доступа может иметьinit
в качестве модификатора доступа. Этотset
метод доступа можно вызывать только из инициализатора объектов или конструкторов типа. Это более строго, чемprivate
наset
метод доступа. - Автоматически реализованное свойство может объявлять
get
метод доступа безset
метода доступа. В этом случае компилятор позволяетset
вызывать метод доступа только из конструкторов типа. Это более строго, чемinit
метод доступа к методуset
доступа.
Измените Person
класс следующим образом:
public class Person
{
public Person(string firstName) => FirstName = firstName;
public string FirstName { get; }
// Omitted for brevity.
}
В предыдущем примере требуется, чтобы вызывающие пользователи использовали конструктор, включающий FirstName
параметр. Вызывающие не могут использовать инициализаторы объектов для назначения значения свойству. Для поддержки инициализаторов можно сделать set
метод доступа методом init
доступа, как показано в следующем коде:
public class Person
{
public Person() { }
public Person(string firstName) => FirstName = firstName;
public string? FirstName { get; init; }
// Omitted for brevity.
}
Эти модификаторы часто используются с модификатором required
для принудительной инициализации.
Обязательные свойства
В предыдущем примере вызывающий объект может создать конструктор Person
по умолчанию без задания FirstName
свойства. Свойство изменило тип на строку, допускаемую значение NULL. Начиная с C# 11, можно требовать, чтобы вызывающие пользователи могли задать свойство:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName) => FirstName = firstName;
public required string FirstName { get; init; }
// Omitted for brevity.
}
Предыдущий код вносит два изменения в Person
класс. Во-первых FirstName
, объявление свойства включает required
модификатор. Это означает, что любой код, создающий новое Person
свойство, должен задать это свойство с помощью инициализатора объектов. Во-вторых, конструктор, принимаюющий firstName
параметр, имеет System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute атрибут. Этот атрибут сообщает компилятору, что этот конструктор задает все required
элементы. Вызывающие объекты, использующие этот конструктор, не требуются для задания required
свойств с инициализатором объектов.
Внимание
Не путайте required
с ненулевой. Допустимо задать required
для свойства null
значение или default
. Если тип не допускает значение NULL, например string
в этих примерах, компилятор выдает предупреждение.
var aPerson = new Person("John");
aPerson = new Person{ FirstName = "John"};
// Error CS9035: Required member `Person.FirstName` must be set:
//aPerson2 = new Person();
Определения текста выражений
Методы доступа к свойствам часто состоят из однострочных инструкций. Методы доступа назначают или возвращают результат выражения. Эти свойства можно реализовать как члены, воплощающие выражение. Определения текста выражения состоят из =>
маркера, за которым следует выражение, назначаемое или извлекаемое из свойства.
Свойства, доступные только для чтения, могут реализовать get
метод доступа в качестве элемента, на основе выражения. В следующем примере реализуется свойство только для Name
чтения в качестве элемента, наследуемого выражением:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string Name => $"{FirstName} {LastName}";
// Omitted for brevity.
}
Это Name
свойство является вычисляемой собственностью. Для резервного поля Name
нет. Свойство вычисляет его каждый раз.
Свойства с резервными полями
Вы можете смешать концепцию вычисляемого свойства с частным полем и создать кэшированное вычисляемое свойство. Например, обновите FullName
свойство таким образом, чтобы строковое форматирование происходило при первом доступе:
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
Эта реализация работает, так как FirstName
свойства доступны LastName
для чтения. Люди могут изменить свое имя. FirstName
Для обновления свойств и LastName
set
разрешений доступа требуется недопустимое значение кэшированного значенияfullName
. Вы изменяете set
методы FirstName
доступа и LastName
свойства, чтобы fullName
поле вычислялось снова:
public class Person
{
private string? _firstName;
public string? FirstName
{
get => _firstName;
set
{
_firstName = value;
_fullName = null;
}
}
private string? _lastName;
public string? LastName
{
get => _lastName;
set
{
_lastName = value;
_fullName = null;
}
}
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
Эта окончательная версия вычисляет свойство FullName
только при необходимости. Если это допустимо, используется ранее вычисляемая версия. В противном случае вычисление обновляет кэшированное значение. Разработчикам, использующим этот класс, не нужно знать подробности реализации. Ни одно из этих внутренних изменений не влияет на использование объекта person.
Начиная с C# 13, можно создавать partial
свойства в partial
классах. Объявление реализации для partial
свойства не может быть автоматически реализованным свойством. Автоматически реализованное свойство использует тот же синтаксис, что и объявление частичного объявления свойства.
Свойства
Свойства — это своего рода интеллектуальные поля в классе или объекте. Из-за пределов объекта они представляются полями в объекте. Однако для реализации свойства можно использовать полную палитру функциональных возможностей C#. Вы можете предоставлять разные уровни доступа, выполнять проверки, отложенное вычисление или любые другие требования, необходимые в вашем сценарии.
- Простые свойства, не требующие пользовательского кода метода доступа, можно реализовать как определения текста выражений или как автоматически реализуемые свойства.
- Свойства позволяют классу предоставлять общий способ получения и задания значений, скрывая при этом код реализации или проверки.
- Метод доступа get используется для возврата значения свойства, а метод доступа set — для присвоения нового значения. Метод доступа к свойству init используется для назначения нового значения только во время построения объекта. Эти методы доступа могут иметь различные уровни доступа. Дополнительные сведения см. в разделе Доступность методов доступа.
- Ключевое слово value используется для определения значения
set
, которое назначается методом доступа.init
- Свойства могут быть доступны для чтения и записи (они имеют оба метода доступа —
get
иset
), только для чтения (они имеют метод доступаget
, но не имеют метода доступаset
) или только для записи (они имеют метод доступаset
, но не имеют метода доступаget
). Свойства, доступные только для записи, редки.
Спецификация языка C#
Дополнительные сведения см. в разделе Свойства в Спецификации языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.