Menu
Coddy logo textTech

Опорные поля

Часть раздела Объектно-ориентированное программирование путешествия по C# на Coddy — урок 17 из 70.

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

public class Product
{
    private decimal _price;  // Опорное поле
    
    public decimal Price
    {
        get { return _price; }
        set { _price = value; }
    }
}

Это выглядит как дополнительная работа по сравнению с автосвойствами, но резервные поля позволяют добавлять логику. Например, вы можете проверять данные перед их сохранением:

public class Product
{
    private decimal _price;
    
    public decimal Price
    {
        get => _price;
        set => _price = value < 0 ? 0 : value;
    }
}

Теперь отрицательные цены автоматически становятся нулевыми. Вы также можете преобразовывать данные при чтении или записи, логировать изменения или инициировать другие действия.

Соглашение об именовании вспомогательных полей (backing field) обычно использует префикс в виде нижнего подчеркивания (_price) или нижний регистр (price), чтобы отличить его от свойства.

Когда вы используете автоматически реализуемые свойства, такие как public decimal Price { get; set; }, компилятор на самом деле создает для вас скрытое резервное поле. Явные резервные поля предоставляют вам то же хранилище с полным контролем над поведением.

challenge icon

Задание

Легко

Давайте создадим систему температурных датчиков, которая использует вспомогательные поля (backing fields) для проверки и преобразования данных при их сохранении и извлечении.

Вы создадите два файла для организации своего кода:

  • TemperatureSensor.cs: Определите класс TemperatureSensor в пространстве имен Sensors. Этот датчик измеряет температуру в градусах Цельсия, но нам нужно гарантировать, что показания остаются в допустимых пределах. Используйте вспомогательные поля для реализации следующего:
    • Свойство Location (string) со вспомогательным полем _location. При установке местоположения, если значение равно null или пустое, сохраните вместо него "Unknown".
    • Свойство Temperature (double) со вспомогательным полем _temperature. При установке температуры ограничьте значение диапазоном от -50 до 150 (если ниже -50, сохраните -50; если выше 150, сохраните 150).
    • Свойство только для чтения TemperatureFahrenheit, которое вычисляет и возвращает температуру в градусах Фаренгейта по формуле: (celsius * 9 / 5) + 32. Оно должно использовать вспомогательное поле напрямую.
    • Конструктор, который принимает местоположение и начальное значение температуры.
  • Program.cs: В основном файле создайте датчик, используя входные значения, и выведите его показания. Затем обновите температуру вторым входным значением и покажите обновленные показания.

Вы получите три входных значения:

  • Местоположение датчика (Sensor location)
  • Начальное показание температуры (Initial temperature reading)
  • Обновленное показание температуры (Updated temperature reading)

Выведите результат в следующем формате:

Sensor: {Location}
Temperature: {Temperature}C ({TemperatureFahrenheit}F)
Updated Temperature: {Temperature}C ({TemperatureFahrenheit}F)

Например, если входные данные — Lab Room, 25 и 200, вывод должен быть следующим:

Sensor: Lab Room
Temperature: 25C (77F)
Updated Temperature: 150C (302F)

Обратите внимание, как второе показание 200 ограничивается значением 150, так как оно превышает максимально допустимую температуру. Ваши вспомогательные поля дают вам такой контроль над тем, как хранятся данные!

Шпаргалка

Резервное поле (backing field) — это закрытое поле, в котором хранятся фактические данные свойства, что позволяет добавлять пользовательскую логику для получения и установки значений.

Базовый синтаксис резервного поля

public class Product
{
    private decimal _price;  // Backing field
    
    public decimal Price
    {
        get { return _price; }
        set { _price = value; }
    }
}

Синтаксис с использованием выражений (Expression-Bodied)

public decimal Price
{
    get => _price;
    set => _price = value;
}

Добавление логики валидации

Резервные поля позволяют проверять или преобразовывать данные перед их сохранением:

private decimal _price;

public decimal Price
{
    get => _price;
    set => _price = value < 0 ? 0 : value;  // Prevent negative prices
}

Соглашение об именовании

Для резервных полей обычно используется префикс в виде нижнего подчеркивания (_price) или нижний регистр (price), чтобы отличить их от имени свойства.

Автоматические свойства против резервных полей

Автоматически реализуемые свойства, такие как public decimal Price { get; set; }, автоматически создают скрытое резервное поле. Явные резервные поля предоставляют то же хранилище, но с полным контролем над поведением.

Попробуйте сами

using System;
using Sensors;

class Program
{
    public static void Main(string[] args)
    {
        // Чтение входных данных
        string location = Console.ReadLine();
        double initialTemp = Convert.ToDouble(Console.ReadLine());
        double updatedTemp = Convert.ToDouble(Console.ReadLine());

        // TODO: Создайте TemperatureSensor с указанием местоположения и начальной температуры

        // TODO: Выведите местоположение датчика и начальные показания температуры
        // Формат: "Sensor: {Location}"
        // Формат: "Temperature: {Temperature}C ({TemperatureFahrenheit}F)"

        // TODO: Обновите температуру датчика новым значением

        // TODO: Выведите обновленные показания температуры
        // Формат: "Updated Temperature: {Temperature}C ({TemperatureFahrenheit}F)"
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование