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

定义编译器合成方法

代码将计算该时间段内正确的采暖和制冷度日数。 但此示例展示了为何需要替换记录的某些合成方法。 可以在记录类型中声明你自己的版本的任意编译器合成方法(clone 方法除外)。 clone 方法具有编译器生成的名称,你无法提供其他实现。 这些合成方法包括复制构造函数、System.IEquatable<T> 接口的成员、相等性和不相等测试以及 GetHashCode()。 为此,你需要合成 PrintMembers。 你还可以声明自己的 ToString,但 PrintMembers 为继承方案提供了更好的选择。 若要提供自己的合成方法版本,签名必须与合成方法相匹配。

控制台输出中的 TempRecords 元素不起作用。 它只显示类型。 可通过提供自己的合成 PrintMembers 方法的实现来更改此行为。 签名取决于应用于 record 声明的修饰符:

如果记录类型为 sealed 或 record struct,则签名为 private bool PrintMembers(StringBuilder builder);

如果记录类型不为 sealed 并派生自 object(即,它不声明基本记录),则签名为 protected virtual bool PrintMembers(StringBuilder builder);

如果记录类型不为 sealed 并派生自其他记录,则签名为 protected override bool PrintMembers(StringBuilder builder);

了解 PrintMembers 的目的之后,就可以轻松地理解这些规则。 PrintMembers 将记录类型中每个属性的相关信息添加到字符串。 该协定要求基本记录添加其要显示的成员,并假设派生成员将添加其成员。 每个记录类型都会合成一个 ToString 替代,与下面的 HeatingDegreeDays 示例类似:

复制代码
public override string ToString()
{
    StringBuilder stringBuilder = new StringBuilder();
    stringBuilder.Append("HeatingDegreeDays");
    stringBuilder.Append(" { ");
    if (PrintMembers(stringBuilder))
    {
        stringBuilder.Append(" ");
    }
    stringBuilder.Append("}");
    return stringBuilder.ToString();
}

在不打印集合类型的 DegreeDays 记录中声明 PrintMembers 方法:

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

签名声明一个 virtual protected 方法来匹配编译器的版本。 如果访问器出错,请不要担心;语言会强制执行正确的签名。 如果你忘记了任何合成方法的正确修饰符,则编译器会发出警告或错误,帮助你获取正确的签名。

在 C# 10 及更高版本中,可以将 ToString 方法声明为 sealed 记录类型。 这会阻止派生记录提供新的实现。 派生记录将仍包含 PrintMembers 替代。 如果不希望 ToString 显示记录的运行时类型,则可以将其密封。 在前面的示例中,你会丢失有关记录测量取暖度日数或降温度日数的位置信息。

非破坏性修改

位置记录类中的合成成员不会修改记录的状态。 目的是帮助你更轻松地创建不可变记录。 请记住,你声明了 readonly record struct 来创建不可变记录结构。 请再次查看前面的关于 HeatingDegreeDays 和 CoolingDegreeDays 的声明。 添加的成员对记录的值执行计算,但不会改变状态。 位置记录使你可以更轻松地创建不可变引用类型。

创建不可变的引用类型意味着需要使用非破坏性修改。 使用 with 表达式 创建与现有记录实例类似的新记录实例。 这些表达式是一个副本构造,其中包含修改副本的其他赋值。 结果是一个新的记录实例,其中每个属性都已从现有记录进行复制并选择性地进行了修改。 原始记录未发生更改。

让我们向程序添加一些演示 with 表达式的功能。 首先,创建一条新记录,使用相同数据计算增长的度日数。 增长的度日数通常使用 41F 作为基准,并测量超出基准的温度。 若要使用相同的数据,可创建一条类似于 coolingDegreeDays 的新记录,但基准温度不同:

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

可将计算得出的度数与在较高基准温度下生成的数字进行比较。 请记住,记录是引用类型,这些副本是浅表副本。 不会复制数据的数组,但两条记录都引用相同的数据。 在另一种场景中,这是一个优势。 对于温度增长的日数,记录前 5 天的总度数非常有用。 可以使用 with 表达式创建具有不同源数据的新记录。 下面的代码将生成这些累计数据的集合,然后显示这些值:

复制代码
// 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);
}

还可使用 with 表达式来创建记录的副本。 请勿指定 with 表达式的大括号之间的任何属性。 这意味着将创建一个副本,并且不会更改任何属性:

复制代码
var growingDegreeDaysCopy = growingDegreeDays with { };

运行已完成的应用程序以查看结果。

总结

本教程介绍了记录的几个方面。 记录为基本用途是存储数据的类型提供了简洁的语法。 对于面向对象的类,基本用途是定义责任。 本教程重点介绍了位置记录,在这种记录中,你可以使用简洁的语法来声明记录的属性。 编译器会合成记录的多个成员,以复制和比较记录。 你可针对记录类型添加所需的任何其他成员。 在明确编译器生成的所有成员都不会改变状态的情况下,可以创建不可变的记录类型。 可借助 with 表达式轻松实现非破坏性修改。

记录提供了另一种定义类型的方法。 使用 class 定义来创建面向对象的层次结构,这些层次结构重点关注对象的责任和行为。 可为数据结构创建 struct 类型,这些数据结构可存储数据,并且足够小,以便进行有效复制。 当你需要基于值的相等性和比较、不需要复制值以及要使用引用变量时,可以创建 record 类型。 当希望某个类型的记录功能足够小,可以高效复制时,可以创建 record struct 类型。

相关推荐
腾讯TNTWeb前端团队2 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
mghio3 小时前
Dubbo 中的集群容错
java·微服务·dubbo
范文杰5 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪5 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪6 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy6 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom7 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom7 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom7 小时前
React与Next.js:基础知识及应用场景
前端·面试·github