Использование типов записей

Tip

Вы новичок в разработке программного обеспечения? Сначала начните с учебников для начинающих. Вы освоитесь с классами, методами и циклами.

Есть опыт на другом языке? В этом руководстве рассматриваются функции записи C#, которые вы используете каждый день: равенство значений, позиционный синтаксис и with выражения.

В этом руководстве вы создадите консольное приложение, которое моделирует ежедневные температуры с помощью записей и структур записей.

В этом руководстве вы узнаете, как:

  • Объявляйте позиционные записи и структуры записей.
  • Создайте небольшую иерархию записей.
  • Используйте равенство и форматирование, созданные компилятором.
  • Используйте with выражения для недеструктивной мутации.

Необходимые условия

Инструкции по установке

В Windows данный файл конфигурации WinGet используется для установки всех необходимых компонентов. Если у вас уже установлено что-то, WinGet пропустит этот шаг.

  1. Скачайте файл и дважды щелкните его, чтобы запустить его.
  2. Прочитайте лицензионное соглашение, введите и, и выберите ввод при появлении запроса на принятие.
  3. Если на панели задач появится мигающий запрос контроля учетных записей пользователей (UAC), разрешите установку продолжить.

На других платформах необходимо установить каждый из этих компонентов отдельно.

  1. Скачайте рекомендуемый установщик на странице загрузки пакета SDK для .NET и дважды щелкните его, чтобы запустить его. Страница загрузки обнаруживает платформу и рекомендует последний установщик для вашей платформы.
  2. Скачайте последнюю версию установщика на домашней странице Visual Studio Code и дважды щелкните его, чтобы запустить его. Эта страница также обнаруживает платформу, а ссылка должна быть правильной для вашей системы.
  3. Нажмите кнопку "Установить" на странице расширения C# DevKit. Откроется код Visual Studio и запрашивается, нужно ли установить или включить расширение. Выберите "Установить".

Создание приложения и первой записи

Создайте папку для приложения, запустите dotnet new consoleи откройте созданный проект.

Добавьте файл с именем DailyTemperature.csи добавьте позиционный readonly record struct параметр для значений температуры:

public readonly record struct DailyTemperature(double HighTemp, double LowTemp)
{
    public double Mean => (HighTemp + LowTemp) / 2.0;
}

Добавьте файл с именем Program.csи создайте примеры данных температуры:

private static DailyTemperature[] data = [
    new DailyTemperature(HighTemp: 57, LowTemp: 30), 
    new DailyTemperature(60, 35),
    new DailyTemperature(63, 33),
    new DailyTemperature(68, 29),
    new DailyTemperature(72, 47),
    new DailyTemperature(75, 55),
    new DailyTemperature(77, 55),
    new DailyTemperature(72, 58),
    new DailyTemperature(70, 47),
    new DailyTemperature(77, 59),
    new DailyTemperature(85, 65),
    new DailyTemperature(87, 65),
    new DailyTemperature(85, 72),
    new DailyTemperature(83, 68),
    new DailyTemperature(77, 65),
    new DailyTemperature(72, 58),
    new DailyTemperature(77, 55),
    new DailyTemperature(76, 53),
    new DailyTemperature(80, 60),
    new DailyTemperature(85, 66) 
];

Этот синтаксис обеспечивает краткое моделирование данных с неизменяемой семантикой значений.

Добавьте поведение в структуру записи

В DailyTemperature.csструктуре записи уже есть вычисленное Mean свойство:

public double Mean => (HighTemp + LowTemp) / 2.0;

Структура record хорошо работает здесь, так как каждое значение небольшое и независимое.

Создайте типы записей для расчетов градусо-дней

Note

Градусо-дни отопления и градусо-дни охлаждения измеряют, насколько средняя дневная температура отклоняется от базовой температуры (обычно 65°F/18°C). Градусо-дни отопления накапливаются в холодные дни, когда среднее значение ниже базы, в то время как градусо-дни охлаждения накапливаются в теплые дни, когда среднее значение выше базы. Эти вычисления помогают оценить потребление энергии для нагрева или охлаждения зданий, что делает их полезными для коммунальных компаний, руководителей зданий и анализа климата.

Создайте файл с именем DegreeDays.cs с иерархией для расчета градусо-дней нагрева и охлаждения.

public abstract record DegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords);

public sealed record HeatingDegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords)
    : DegreeDays(BaseTemperature, TempRecords)
{
    public double DegreeDays => TempRecords.Where(s => s.Mean < BaseTemperature).Sum(s => BaseTemperature - s.Mean);
}

public sealed record CoolingDegreeDays(double BaseTemperature, IEnumerable<DailyTemperature> TempRecords)
    : DegreeDays(BaseTemperature, TempRecords)
{
    public double DegreeDays => TempRecords.Where(s => s.Mean > BaseTemperature).Sum(s => s.Mean - BaseTemperature);
}

Теперь вычислите суммы с помощью метода Main в Program.cs:

var heatingDegreeDays = new HeatingDegreeDays(65, data);
Console.WriteLine(heatingDegreeDays);

var coolingDegreeDays = new CoolingDegreeDays(65, data);
Console.WriteLine(coolingDegreeDays);

Сгенерированные ToString данные полезны для быстрой диагностики в процессе итерации.

Переопределите PrintMembers, чтобы настроить выход.

Если выходные данные по умолчанию содержат слишком много шума, переопределите PrintMembers в базовой записи:

protected virtual bool PrintMembers(StringBuilder stringBuilder)
{
    stringBuilder.Append($"BaseTemperature = {BaseTemperature}");
    return true;
}

Переопределение сохраняет выходные данные, ориентированные на необходимые сведения.

Использование с выражениями для недеструктивной мутации

Используется with для создания измененных копий без изменения исходной записи:

// Growing degree days measure warming to determine plant growing rates
var growingDegreeDays = coolingDegreeDays with { BaseTemperature = 41 };
Console.WriteLine(growingDegreeDays);

Расширьте эту идею, чтобы вычислить скользящие итоги из срезов входных данных:

// showing moving accumulation of 5 days using range syntax
List<CoolingDegreeDays> movingAccumulation = new();
int rangeSize = (data.Length > 5) ? 5 : data.Length;
for (int start = 0; start < data.Length - rangeSize; start++)
{
    var fiveDayTotal = growingDegreeDays with { TempRecords = data[start..(start + rangeSize)] };
    movingAccumulation.Add(fiveDayTotal);
}
Console.WriteLine();
Console.WriteLine("Total degree days in the last five days");
foreach(var item in movingAccumulation)
{
    Console.WriteLine(item);
}

Этот подход полезен при необходимости преобразований при сохранении исходных значений.

Дальнейшие действия