windows C#-创建记录类型(上)

记录是使用基于值的相等性的类型。 C# 10 添加了 record structs,以便你可以将记录定义为值类型。 两个记录类型的变量在它们的类型和值都相同时,它们是相等的。 两个类类型的变量如果引用的对象属于同一类类型并且引用相同的对象,则这两个变量是相等的。 基于值的相等性意味着可能需要的记录类型中的其他功能。 声明 record 而不是 class 时,编译器将生成许多这些成员。 编译器针对 record struct 类型生成这些相同的方法。

必备条件

你需要将计算机设置为运行 .NET 6 或更高版本,包括 C# 10 或更高版本编译器。 自 Visual Studio 2022 或 .NET 6 SDK 起,开始提供 C# 10 编译器。

记录的特征

通过使用 record 关键字声明类型,修改 class 或 struct 声明,可以定义记录。 (可选)可以省略 class 关键字来创建 record class。 记录遵循基于值的相等性语义。 为了强制执行值语义,编译器将为记录类型(record class 类型和 record struct 类型)生成多种方法:

  • Object.Equals(Object) 的替代。
  • 一个虚拟的 Equals 方法,其参数为记录类型。
  • Object.GetHashCode() 的替代。
  • 用于 operator == 和 operator != 的方法。
  • 记录类型实现 System.IEquatable<T>。

记录还提供了 Object.ToString() 的重写。 编译器使用 Object.ToString() 合成用于显示记录的方法。 在编写本教程的代码时,你将浏览这些成员。 记录支持 with 表达式,以启用记录的非破坏性修改。

还可使用更简洁的语法来声明位置记录。 声明以下位置记录时,编译器会合成更多方法:

  • 主构造函数,它的参数与记录声明上的位置参数匹配。
  • 主构造函数的每个参数的公共属性。 对于 record class 和 readonly record struct 类型,这些属性为 init-only。 对于 record struct 类型,它们是可读写的。
  • 用于从记录中提取属性的 Deconstruct 方法。

生成温度数据

数据和统计信息是你要使用记录时所需的内容。 在本教程中,你将构建一个用于计算度日数的应用程序,以用于不同用途。 度日数是反映几天、几周或几个月内采暖(或采暖不足)的度量。 度日数可跟踪和预测能源使用情况。 高温天数越多表示使用空调的时间越多,降温天数越多意味着使用暖气炉的时间越多。 度日数有助于管理植物种群,并且随着季节的变化,与植物的生长密切相关。 度日数有助于跟踪动物为适应气候而进行的物种迁徙。

此公式基于给定的某一天的平均温度和基准温度。 若要计算一段时间内的度日数,需要这段时间的每日最高温度和最低温度。 首先,我们要创建一个新的应用程序。 生成新的控制台应用程序。 在名为“DailyTemperature.cs”的新文件中创建新的记录类型:

public readonly record struct DailyTemperature(double HighTemp, double LowTemp);

上述代码定义了位置记录。 由于不打算从 DailyTemperature 记录继承并且该记录应该不可变,因此该记录为 readonly record struct。 HighTemp 和 LowTemp 属性是 init-only 属性,这意味着可在构造函数中设置它们,或使用属性初始化表达式设置它们。 如果希望位置参数是可读写的,则声明 record struct 而不是 readonly record struct。 DailyTemperature 类型还有一个主构造函数,该构造函数具有两个与这两个属性匹配的参数。 使用该主构造函数初始化 DailyTemperature 记录。 下列代码将创建并初始化多个 DailyTemperature 记录。 第一个使用命名参数来阐明 HighTemp 和 LowTemp。 剩余的初始值设定项使用位置参数来初始化 HighTemp 和 LowTemp:

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 记录:

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

现在需确保你可以使用此数据。 将以下代码添加到 Main 方法:

foreach (var item in data)
    Console.WriteLine(item);

运行应用程序,然后你将看到类似于以下显示内容的输出(因空间有限,删除了几行内容):

DailyTemperature { HighTemp = 57, LowTemp = 30, Mean = 43.5 }
DailyTemperature { HighTemp = 60, LowTemp = 35, Mean = 47.5 }


DailyTemperature { HighTemp = 80, LowTemp = 60, Mean = 70 }
DailyTemperature { HighTemp = 85, LowTemp = 66, Mean = 75.5 }

上述代码显示了由编译器合成的 ToString 的替代输出。 如果希望使用不同的文本,可编写自己的 ToString 版本,以防止编译器为你合成一个版本。

计算度日数

若要计算度日数,需要获得给定的某一天的基准温度和平均温度之间的差额。 若要测量一段时间内的采暖,需要忽略平均温度低于基准温度的任何日期。 若要测量一段时间内的降温,需要忽略平均温度高于基准温度的任何日期。 例如,美国使用 65F 作为采暖和制冷度日数的基准。 在此温度下,无需采暖或制冷。 如果某一天的平均温度为 70F,则这一天的制冷度日数为 5,采暖度日数为 0。 相反,如果某一天的平均温度为 55F,则这一天的采暖度日数为 10,制冷度日数为 0。

可将这些公式表示为记录类型的小型层次结构:一种抽象度日数类型以及两种具体的采暖度日数和制冷度日数类型。 这些类型也可以是位置记录。 它们将基准温度和一系列每日温度记录作为主构造函数的参数:

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);
}

抽象的 DegreeDays 记录是 HeatingDegreeDays 和 CoolingDegreeDays 记录的共享基类。 派生记录的主构造函数声明显示了如何管理基本记录初始化。 派生记录为基本记录主构造函数中的所有参数声明参数。 基本记录声明并初始化这些属性。 派生记录不会隐藏它们,而只会创建和初始化未在其基本记录中声明的参数的属性。 在此示例中,派生记录不会添加新的主构造函数参数。 通过将以下代码添加到 Main 方法来测试代码:

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

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

 将获得类似以下显示内容的输出:

HeatingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 85 }
CoolingDegreeDays { BaseTemperature = 65, TempRecords = record_types.DailyTemperature[], DegreeDays = 71.5 }

 

上一篇:web 远程调试工具PageSpy 实战经验


下一篇:为什么要使用Ansible实现Linux管理自动化?