Свойства (Руководство по программированию в 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#.

См. также