JavaRush /Курсы /C# SELF /required свойства и <...

required свойства и field свойства

C# SELF
17 уровень , 3 лекция
Открыта

1. Введение

Программировать на C# без свойств можно, но это примерно как ездить на роликах по Белому Дому — можно, но некомфортно. Мало того, что мы теперь привыкли к короткому синтаксису без лишних геттеров и сеттеров, но когда наше приложение начинает работать с «серьёзными» моделями (например, классом User, описывающим пользователя в системе), нам становится важно гарантировать, что все нужные данные действительно присутствуют.

К примеру, если у нас есть класс:

public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Можно легко забыть инициализировать свойства:

User user = new User(); // Name будет null, Age = 0

В результате где-нибудь через десять экранов и сто поворотов логики вы получите знаменитый NullReferenceException и будете долго искать виноватого.

Вспомним про init-only свойства

Да, мы можем использовать инициализацию только при создании:

public string Name { get; init; }

Но даже с этим синтаксисом никто не заставит пользователя класса передать значение, если он не захочет — большинство конструкторов по умолчанию инициализируют поля значениями по умолчанию (null, 0 и т.д.).

Так как же обязать программиста или даже себя самого не забыть задать нужное значение? Именно для этого и был изобретён модификатор required, появившийся в C# 11.

2. required свойства: строгая инициализация

Модификатор required — это указание компилятору следить за тем, чтобы конкретное свойство объекта было явно установлено в момент его создания. Проще говоря, если такое свойство не инициализировано при создании объекта — компилятор вас не пропустит, а IDE нарисует страшную красную волну.


public class User
{
    public required string Name { get; set; }
    public int Age { get; set; }
}
Обязательное свойство с required

Попробуем создать пользователя:

// Ошибка компиляции: свойство Name обязательно к инициализации!
User user1 = new User();

Или вот так:


// Ошибка компиляции: не указано required-свойство Name
User user2 = new User { Age = 18 };

А вот корректный способ:

User user3 = new User { Name = "Гермиона", Age = 18 };

Как работает required?

Модификатор required говорит компилятору: «После завершения конструктора объекта (любого!), вот это свойство должно быть явно задано».

Это работает как для обычных свойств, так и для init-only:

public required string Name { get; init; }

Если вы определяете пользовательский конструктор, инициализирующий значение required-свойства, то правило тоже соблюдено.

Типичная схема проверки

Сценарий Компилятор доволен?
Не задано required-свойство
Задано в объектном инициализаторе ✔️
Задано в конструкторе ✔️

Пример в нашей «Собачьей» модели

public class Dog
{
    public required string Name { get; set; }
    public int Age { get; set; }
}

Dog dog = new Dog { Name = "Бобик", Age = 5 }; // Всё ок!
Dog badDog = new Dog { Age = 2 }; // Ошибка! Не указано Name

Где реально помогает required?

  • Передача DTO между слоями: Если у вас API, необходимо, чтобы на вход всегда поступали все нужные поля.
  • Сложные модели с обязательными атрибутами: Например, Product с обязательным SKU, Order с обязательным номером.
  • На собеседованиях и ревью: Если вы показали такой синтаксис, скорее всего, ваш код будут читать с уважением (и лёгкой завистью).

3. Как required работает с конструкторами?

Иногда вы явно пишете конструктор. Что произойдет, если required-свойство не инициализировать в конструкторе или объектном инициализаторе? Компилятор выдаст ошибку.


public class Article
{
    public required string Title { get; set; }
    public required string Author { get; set; }

    public Article()
    {
        // Если не инициализировать Title и Author — ошибка компиляции!
        // Можно так:
        Title = "Без названия";
        Author = "Неизвестный";
    }
}

Если конструктор сам присваивает значения required-свойствам — всё отлично. Если нет, то вы должны инициализировать эти поля через объектный инициализатор (new Article { ... }).

Специфика использования

  • required работает только со свойствами, а не с полями.
  • required не наследуется — если базовое свойство required, а в наследнике вы не указали required, компилятор не ругается (но хорошей практикой будет явно повторить required в наследнике).
  • required нельзя применить к автоматическим полям или к чему-нибудь ещё кроме property.

4. Стрелочная запись свойств

С появлением новых версий C# разработчики всё чаще стремятся к лаконичному и выразительному коду. Одно из самых заметных нововведений для свойств — стрелочная запись (или expression-bodied properties).

Иногда хочется определить свойство, которое просто возвращает значение без какой-либо дополнительной логики. Раньше для этого приходилось писать полный геттер с фигурными скобками:

public int Age
{
    get { return birthYear > 0 ? DateTime.Now.Year - birthYear : 0; }
}

Теперь можно писать гораздо короче — используя стрелку (=>):


public int Age => birthYear > 0 ? DateTime.Now.Year - birthYear : 0;

Такой синтаксис называется свойство с телом-выражением (expression-bodied property). Он отлично подходит для простых вычислений и делает код компактнее.

Пример использования get и set

public class Book
{
    private string _title;

    public string Title
    {
        get => _title;
        set => _title = value.Trim();
    }
}

Здесь get возвращает значение поля, а set — присваивает, предварительно убирая лишние пробелы по краям.

Пример использования только get (только для чтения):

Если свойство только для чтения — его можно записать вообще без фигурных скобок, просто через =>.

public class Person
{
    private string name = "Марк Твен";

    // Только для чтения: вычисляемое свойство
    public string Name => name.ToUpper();
}

Здесь свойство Name можно только прочитать — оно всегда возвращает name в верхнем регистре.

5. Ключевое слово field для свойств

До C# 14, если вы хотели обратиться к скрытому полю автоматического свойства прямо в сеттере или геттере (например, чтобы защититься от рекурсии или добавить свою логику) — это было невозможно. Вам приходилось объявлять поле явно.

C# 14 разрешает обращаться к скрытому полю автоматического свойства с помощью ключевого слова field:


public class Person
{
    public string Name
    {
        get => field; // field — это скрытое поле свойства Name
        set
        {
            if (string.IsNullOrWhiteSpace(value))
                throw new ArgumentException("Имя не может быть пустым!");
            field = value; // используем field вместо явного _name
        }
    }
}

Раньше пришлось бы вот так:

private string _name;
public string Name
{
    get => _name;
    set
    {
        if (string.IsNullOrWhiteSpace(value))
            throw new ArgumentException("Имя не может быть пустым!");
        _name = value;
    }
}

Стало на одно объявление переменной меньше. Чем код компактнее, тем лучше.

Почему важно иногда обращаться именно к полю внутри свойства?

  • Иногда нужно явно контролировать где и как хранится значение (например, если вы хотите выдавать копию объекта, кэшировать результат или делать lazy-loading).
  • Если вы хотите использовать атрибуты или reflection — поле может понадобиться по имени.
  • В некоторых случаях (де)сериализации или performance-tunings вы хотите иметь больше контроля над хранением значения.

Варианты использования:

Валидация данных в сеттере

public double Grade
{
    get => field;
    set
    {
        if (value < 0 || value > 5)
            throw new ArgumentOutOfRangeException("Оценка должна быть от 0 до 5");
        field = value;
    }
}

Статистика изменения значения

public int StepCount
{
    get => field;
    set
    {
        if (value > field)
        {
            Console.WriteLine($"Ура! Вы сделали {value - field} шагов больше!");
        }
        field = value;
    }
}

Lazy Load

public string Data
{
    get
    {
        if (field == null)
            field = LoadDataFromDatabase();
        return field;
    }
    set => field = value;
}

6. Типичные ошибки и нюансы

Ошибка №1: забыли инициализировать required-свойство.
Компилятор не даст пройти с таким кодом и выдаст ошибку сразу при сборке, что помогает избежать проблем во время работы программы.

Ошибка №2: required-свойства не работают без полной инициализации в конструкторе.
Если конструктор не принимает значения для всех обязательных свойств, компилятор напомнит, что вы что-то упустили.

Ошибка №3: попытка использовать required с const или readonly.
Эти модификаторы несовместимы — required можно применять только к обычным свойствам. Попытка их сочетать приведёт к ошибке.

2
Задача
C# SELF, 17 уровень, 3 лекция
Недоступна
Создание класса с required-свойствами
Создание класса с required-свойствами
2
Задача
C# SELF, 17 уровень, 3 лекция
Недоступна
Пользовательская логика в геттерах и сеттерах с использованием field
Пользовательская логика в геттерах и сеттерах с использованием field
Комментарии (8)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Ra Уровень 35 Student
20 ноября 2025
ИИ отстой. "Иногда хочется определить свойство, которое просто возвращает значение без какой-либо дополнительной логики." - И далее пишется геттер с логикой.
Ra Уровень 35 Student
20 ноября 2025
Что означает выражение "required нельзя применить к автоматическим полям" ?? Поля же не бывают автоматическими? public required int Age; // поле, не автоматическое, всё компилируется. Тоже ИИ-шные галлюцинации
Yurii N Уровень 32
6 января 2026
Хорошо подмечено. Все три утверждения — неверные (или вводят в заблуждение): "Специфика использования required работает только со свойствами, а не с полями. required не наследуется — если базовое свойство required, а в наследнике вы не указали required, компилятор не ругается (но хорошей практикой будет явно повторить required в наследнике). required нельзя применить к автоматическим полям или к чему-нибудь ещё кроме property."
Дмитрий Уровень 60
28 августа 2025
Во второй задаче "Пользовательская логика в геттерах и сеттерах с использованием field" В тексте задачи несколько раз акцентируется, что должен быть использован field. Но валидатор хочет чтобы использовалось приватное поле в свойстве. Соответственно, решение с field не проходит проверку. Вторая проблема возникшая у меня с field: пришлось в проект добавлять <LangVersion>preview</LangVersion> чтобы Rider не ругался на field.
Николай Уровень 28
8 сентября 2025
Пока такое же поведение. Еще не исправлено
Ra Уровень 35 Student
20 ноября 2025
+1
Ilya Уровень 31
30 ноября 2025
+1
Александр Уровень 23
1 декабря 2025
дак там явно указано в задаче "приватные поля ('field')" field не является приватным полем, оно является скрытым полем как бы задание путает? - да. ошибка? - нет. не надо додумывать за ПМа и/или за заказчика, это всегда плохо заканчивается)