Свойства (Руководство по программированию в 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.
}
Свойства, поддерживаемые полем
В C# 13 можно добавить проверку или другую логику в метод доступа для свойства с помощью функции предварительного field
просмотра ключевых слов. Ключевое field
слово обращается к синтезированному полю резервного копирования компилятора для свойства. Он позволяет создавать метод доступа к свойствам без явного объявления отдельного поля резервного копирования.
public class Person
{
public string? FirstName
{
get;
set => field = value.Trim();
}
// Omitted for brevity.
}
Внимание
Ключевое field
слово — это предварительная версия функции в C# 13. Чтобы использовать контекстное ключевое слово field
, вы должны использовать .NET 9 и установить элемент <LangVersion>
на preview
в вашем файле проекта.
Следует осторожно использовать возможность ключевого слова field
в классе, имеющем поле под именем field
. Новое field
ключевое слово скрывает поле с именем field
в контексте функции доступа к свойству. Можно изменить имя переменной field
или использовать @
маркер для ссылки на field
идентификатор как @field
. Дополнительные сведения см. в спецификации компонента для ключевого field
слова.
Обязательные свойства
В приведенном выше примере вызывающий объект может создать экземпляр 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
. Свойство вычисляет его каждый раз.
Управление доступом
В предыдущих примерах показаны свойства чтения и записи. Вы также можете создать свойства только для чтения или задать разные уровни доступа для методов доступа set и get. Предположим, что класс Person
должен позволять изменять значение свойства FirstName
только в других методах класса. Вы можете предоставить доступность аксессора set для private
, вместо internal
или 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
аксессора. В этом случае компилятор позволяет вызывать аксессор только из конструкторов типа. Это более ограничительно, чем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
для принудительной инициализации.
Свойства с резервными полями
Вы можете смешать концепцию вычисляемого свойства с частным полем и создать кэшированное вычисляемое свойство. Например, обновите 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#.