JavaRush /Курсы /C# SELF /Автоматические свойства и init-only сеттеры

Автоматические свойства и init-only сеттеры

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

1. Автоматические свойства

В C# свойства (property) — это что-то среднее между полем и методом. По сути, это синтаксический сахар, который делает работу с инкапсуляцией максимально удобной. Вы обращаетесь к свойству так, будто это обычное поле, а внутри — может быть любая логика: проверка данных, изменение других полей, вызов методов и даже отправка e-mail вашей бабушке (последнее — не рекомендуется). В прошлой лекции мы писали свойства вручную. Но это может быстро приесться, если у тебя 100500 простых объектов, где нужны только "геттер" и "сеттер" — без особой логики.

Встречайте — автоматические свойства. Они избавят вас от шаблонного кода и позволят C# заботиться о хранении значения без вашего участия.

Как было раньше: ручные свойства

Когда мы хотим спрятать поле, но дать к нему доступ через свойство, обычно пишем вот так:


public class Dog
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}
Ручное свойство с приватным полем

Подобный код — типичнейшее "занудство" (boilerplate). А ведь зачастую нам ничего "добавлять" в эти get/set не нужно! Просто дать доступ — и всё.

Как можно теперь

В C# всё стало проще: теперь не нужно вручную прописывать поля — компилятор берёт это на себя. Достаточно написать:


public class Dog
{
    public string Name { get; set; }
}
Автоматическое свойство

И всё готово. Можно спокойно читать и менять значение Name, при этом под капотом компилятор сам создаёт приватное поле, которое снаружи никак не достать напрямую.

Что же на самом деле происходит за кулисами? Когда в коде появляется public string Name { get; set; }, компилятор автоматически создаёт скрытое поле вроде Name__BackingField. При каждом обращении к Name он вставляет нужный геттер или сеттер. Само поле напрямую в коде не видно, но оно существует — и с помощью рефлексии его даже можно найти (но это уже совсем другая история).

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

2. Синтаксис автоматических свойств

Синтаксис очень простой:


public class Dog
{
    public string Name { get; set; }
    public int Age { get; set; }
}
Несколько автоматических свойств

Теперь наш Dog выглядит гораздо лаконичнее! Мы можем создавать объекты и задавать значения привычно:

Dog myDog = new Dog();
myDog.Name = "Барбос";
myDog.Age = 4;

Схематично

Способ Синтаксис Доступ к значению
Поле
public string Name;
Доступен напрямую
Ручное свойство
public string Name { get; set; } + private string name;
Доступ через get/set
Автосвойство
public string Name { get; set; }
get/set + скрытое поле

3. Кастомизация доступа: только чтение, только запись

Только для чтения: get-only

Если свойство нужно только читать (например, регистрационный номер питомца), можно сделать его доступным только для чтения — указав get, а set либо вообще не писать, либо сделать приватным. Такое значение можно присваивать только в конструкторе или прямо при объявлении:


public class Dog
{
    public string RegistrationCode { get; }

    public Dog()
    {
        RegistrationCode = "DOG-001"; // Присваиваем значение в конструкторе
    }
}

Теперь извне можно только прочитать RegistrationCode, но не изменить. Отличный способ защитить важные данные от случайных изменений.

Ещё вариант: приватный set


public string Name { get; private set; }
Приватный сеттер — изменяемо только внутри класса

Тогда свойство можно изменить только внутри класса (например, через метод или конструктор).

Только для записи: вредная затея

Чисто теоретически можно сделать set-only свойство (только для записи), но это встречается крайне редко и не рекомендуется — представьте объект, который ведёт себя как "чёрная дыра": туда можно что-то выкинуть, но достать обратно — никогда.

4. init-only сеттеры

Немного предыстории

Раньше, чтобы сделать неизменяемый объект (immutable), нужно было писать только get-свойства и задавать значения через конструктор. ОК, удобно — но не всегда. Но недавно в C# появился новый синтаксис: init-only свойства.

В чём суть init-сеттера?

  • Позволяет установить значение свойства только при создании объекта (т.е. в конструкторе или в инициализаторе объекта).
  • После создания объекта свойство становится только для чтения.
  • Отлично подходит для "почти неизменяемых" классов, DTO, конфигураций, моделей.

public class Dog
{
    public string Name { get; init; }
    public int Age { get; init; }
}
Свойства с init-сеттерами

Теперь мы можем сделать так:

Dog dog = new Dog { Name = "Бобик", Age = 2 };
dog.Name = "Рэкс"; // Ошибка! После создания - только чтение

В конструкторе тоже можно задавать значения:

public Dog(string name, int age)
{
    Name = name;
    Age = age;
}

Схема: когда можно присваивать

Где присваивать? { get; set; } { get; private set; } { get; init; }
Вне класса, после создания
В конструкторе
В инициализаторе
Внутри класса ❌ (кроме конструктора)

5. Применение на практике

Давайте перепишем наш класс Dog, используя автоматические и init-only свойства:


public class Dog
{
    // Свойство, устанавливается только при инициализации
    public string Name { get; init; }
    public int Age { get; init; }

    // Автоматическое свойство для текущего состояния
    public bool IsHungry { get; set; }

    // Метод, действия собаки
    public void Bark()
    {
        Console.WriteLine($"{Name} говорит: Гав!");
    }
}

Теперь мы можем создавать Dog вот так:

Dog dog = new Dog { Name = "Шарик", Age = 3, IsHungry = true };

dog.Bark(); // выведет: Шарик говорит: Гав!
dog.IsHungry = false; // это свойство можно менять после создания
dog.Name = "Барбос"; // Ошибка! Свойство только для инициализации

6. Автоматические свойства с начальными значениями

В C# все сделано для вашего удобства. Можно даже сразу задать начальное значение свойству:


public class Dog
{
    public string Name { get; set; } = "Безымянный";
    public int Age { get; set; } = 0;
}
Автоматические свойства с начальными значениями

Или с init:


public class Dog
{
    public string Name { get; init; } = "Щенок";
    public int Age { get; init; } = 0;
}
init-only свойства с начальными значениями

Теперь если не указать Name при создании, оно будет "Щенок".

7. Автоматические свойства с разным уровнем доступа

Можно делать так, чтобы свойство читалось и писалось с разными модификаторами доступа:


public class Dog
{
    public string Name { get; private set; }
    public int Age { get; private set; }
    
    public Dog(string name, int age)
    {
        Name = name;
        Age = age;
    }
}

В данном случае изменить имя и возраст можно только внутри класса Dog, например, в методах или конструкторе.

Сравнение ручных и автоматических свойств

Подход Сложность написания Контроль в set/get Изменяемость после создания Использование в моделях
Ручное свойство Длинно Абсолютный Любая Там, где нужна логика
Автоматическое свойство Коротко Нет Любая или приватная Почти везде
init-only автоматическое Коротко Нет Только при создании DTO, конфиги

8. Типичные ошибки при работе со свойствами

Ошибка №1: попытка присвоить значение get-only свойству вне конструктора.
Если свойство объявлено только с get, компилятор не позволит изменить его значение где-то ещё. Присваивать можно только в конструкторе или прямо при объявлении. Забыл — получишь ошибку компиляции.

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

Ошибка №3: приватный set без понимания последствий.
Если у свойства есть приватный set, а в остальном коде всё ещё пытаются присваивать ему значения — будет ошибка. Часто это происходит случайно, особенно при копировании чужого кода. Всегда проверяй, доступен ли set там, где ты собираешься использовать его.

2
Задача
C# SELF, 17 уровень, 2 лекция
Недоступна
Использование init-only свойств
Использование init-only свойств
2
Задача
C# SELF, 17 уровень, 2 лекция
Недоступна
Автоматические свойства с начальными значениями
Автоматические свойства с начальными значениями
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ