引言
C# 9.0 版本带来了一些非常实用的新特性,其中最引人注目的莫过于record。这是一种新型的数据类型,专门用于表示“记录”数据,以及可以设置许多与属性相关的缺省值和模式匹配操作,使用起来非常方便。
本文主要讲解 C# 9 新特性中 record 的一些基础知识、语法、应用场景等内容。
C# 9.0 新特性 —— record
record 是 C#9.0 新加入的一种新类型,是 struct 的一种变种,主要用于表示不可变的数据。
record 和 struct 和 class 一样,都是定义一种新的数据集合,但它有一些特殊的属性:
- record 可以声明为不可变的类型(只读)
- record 的相等比较是值类型比较,而非引用类型比较。
- record 的基本构造函数是带参数的,它自己有很多构造函数。
- 在模式匹配(pattern matching)分支语句中,record 有额外的选项。
如果您在使用 .NET Core 3.1 和 C# 9.0,则需要将以下行添加到 csproj 文件中,以启用 C# 9.0。
<PropertyGroup>
<LangVersion>9.0</LangVersion>
</PropertyGroup>
类 vs record
假设我们要编写一个应用程序,用于存储和显示一些长方形信息。我们可以定义 Rectangle 类,其中包含 Width 和 Height 属性。以下是该代码的示例:
public class Rectangle {
public int Width { get; set; }
public int Height { get; set; }
public Rectangle(int width, int height) {
Width = width;
Height = height;
}
}
此时,我们可以使用以下代码创建一个 Rectangle 对象:
Rectangle rect = new Rectangle(100, 150);
也可以更改 rect 的 Width 和 Height 属性:
rect.Width = 150;
但是,Rectangle 类有一个问题,那就是它是可变的,对象状态可以在代码中更改。换句话说,我们可以在程序的某个地方,修改 Width 和 Height 属性的值,这会导致 Rectangle 类的可变性,这在我们不希望修改某个对象状态时可能会导致问题。
为了解决这个问题,我们可以将 Rectangle 类转换为 record。
public record Rectangle(int Width, int Height);
这里我们定义了一个具有 Width 和 Height 两个属性的 record。这个属性不能更改,它是一个不可变的类型。这是因为我们仅仅可以从构造函数获取 record 的值,所以不允许使用默认的 setter 方法来更改 record 中的属性值。
定义 record 后,我们可以使用以下代码创建 Rectangle 对象。
Rectangle rect = new Rectangle(100, 150);
我们可以在代码的任何地方使用“新实例化”创建 Rectangle 对象,并且在声明后,它的属性名称和值都不能被修改。这意味着,rect 现在是一个不可变对象,在代码的任何其它地方,如果未经允许改变其状态,将不会影响到该对象。
record 类型语法介绍
record 类型的语法有五种。
1. 默认 record 类型
默认 record 类型类似如下代码。
public record Person {
public string FirstName { get; init; }
public string LastName { get; init; }
public int Age { get; init; }
}
这里我们定义了一个名为 Person 的 record。Person 有 3 个属性:FirstName、LastName 和 Age。
1.1. init 属性
record 中的属性,可以使用 readonly 属性或可写的 init 属性设置只读(不可修改)状态。init 只能在构造函数中分配。
2. 可以增加一个 positional record 类型
位置记录的定义方式类似如下代码:
public record Person(string FirstName, string LastName, int Age);
3. 可以扩展 record 类型:
扩展 record 类型的方法类似如下
public record Person : ICloneable {
public string FirstName { get; init; }
public string LastName { get; init; }
public int Age { get; init; }
public object Clone() {
return this.MemberwiseClone();
}
}
这里,Person 继承了 ICloneable 接口,并实现了该接口的 Clone() 方法。
4. record 类型可以是泛型:
定义泛型 record 类型的方法如下:
public record Person<T>(string FirstName, string LastName, T Age);
5. record 类型可以定义私有构造函数和其他类成员:
public record Student {
public string FirstName { get; init; }
public string LastName { get; init; }
public int Age { get; init; }
private int _score;
public Student(string firstName, string lastName, int age, int score) {
FirstName = firstName;
LastName = lastName;
Age = age;
_score = score; // 带有非默认的构造函数
}
}
with表达式
使用 with 表达式来创建一个新的 record 对象,可以保留原始 record 对象的值,并根据需要更改一些属性。
以下是一个示例:
public record Person {
public string FirstName { get; init; }
public string LastName { get; init; }
public int Age { get; init; }
}
Person person = new Person { FirstName = "Tony", LastName = "Stark", Age = 45 };
Person person2 = person with { LastName = "王" };
在这个例子中,我们定义了一个名为 Person 的 record 类型。我们创建了一个名为 person 的 Person 对象,FirstName、LastName 和 Age 分别被初始化为 "Tony"、"Stark" 和 45。
我们使用 with 表达式创建了另一个名为 person2 的 Person 对象,在这个新对象中我们仅更改了 LastName 属性。
记录变量 Person2 其余的值(即 FirstName 和 Age)来自 person。因此,person2 与 person 具有相同的值的属性,但有不同的 LastName 值。记住,这不会更改原始 record 的值,它只会创建一个新的 record 对象。
record struct
record struct 类似对结构体的定义,仅具有存储字段,而没有方法。
public record struct Foo {
private int _value;
public Foo(int value) {
_value = value;
}
public int DoubleValue => _value * 2;
}
总结
Record 是一种非常有用的类型,在 C# 9.0 版本中,提供了一些很好的优化。使用 Record,我们主要可以得到以下两个好处。
其一,在读取你输入的对象时,如果其中的数据结构类似于 Record,那么就可以非常方便地为其设置默认值和构造函数,将同样类型的数据作为一个 Record 类型来处理。
其二,使用 Record 类型,我们可以实现一些非常高效的编程。这是因为 Record 可以通过引用传递而不是 clone。如上所述,不能更改 Record 中属性的值,而且引用传递将有助于避免发生不必要的克隆操作。因此,使用 Record 允许我们将一些应该不变的数据类型做成不可变的对象。
以上就是 C# 9 中 Record 的相关总结。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C# 9 新特性——record的相关总结 - Python技术站