9 有一个新的数据类型,叫做记录(Record)。这个类型是一种特殊的引用类型,我们只需要给出一个东西的具体属性,就可以自动为这个类型生成指定的比较器(Equals 方法、比较运算符 operator == 和 operator !=、GetHashCode 方法,甚至是 ToString 方法等等)。
举个例子,我们可以这么写:
public sealed record Point(int X, int Y);
这就等价于一个类 Point,然后生成 X 和 Y 属性,以及相关的方法:
public sealed class Point
{
public int X { get; init; }
public int Y { get; init; }
public override string ToString() => $codeholder_1#34;Point {{ X = {X}, Y = {Y} }}";
public override bool Equals(object? obj) => obj is Point p && p.X == X && p.Y == y;
public override int GetHashCode() => HashCode.Combine(X, Y);
public static bool operator ==(Point l, Point r) => l.Equals(r);
public static bool operator !=(Point l, Point r) => !(l == r);
}
这样的东西。你看看,就写一句话就能生成一系列的内容,是不是很方便。
不过,Point 类型就俩属性,显然没有必要定义成类,因为它太轻量级了。因此,C# 10 开始允许结构记录类型。
public record struct Point(int X, int Y);
这样就好比把前文的 sealed class 改写成 struct。因此 C# 10 开始允许结构的记录类型,所以更轻量级,灵活度更高了。当然,C# 10 依然允许引用类型(类)的记录类型,你可以使用 record 或者 record class 来表示一个类的记录类型。
前文用到的 init 属性是 C# 9 里诞生的、用来表示属性只在初始化器和构造器里才可赋值的一种属性赋值模型。它比 set 的使用范围要少,set 随时随地都可以赋值(最多加一个访问修饰符,但并不能阻止内部的任何时候的赋值)。init 仅允许初始化器和构造器里使用赋值,其它任何地方都不能赋值。这样做就避免了很多地方的安全问题,同时也提供了一种语法上的约束.
全局 using 指令
C# 很早(C# 2)就允许类型取别名。但由于这个东西实在是不好用(只允许同文件取别名),因此不是很常用。C# 10 开始允许全局的 using 指令。
你只需要在单独的文件里写上这么一句话:
global using IntegerList = System.Collections.Generic<int>;
global using IntegerList = System.Collections.Generic<int>;
那么,整个项目就都可以使用这个 IntegerList 了。
namespace 指令
用一句类似 using 指令的形式,对文件整体进行声明命名空间,这样的话,后续的内容就不用再用大括号了
namespace Test;
class S
{
// ...
}
等价于
namespace Test
{
class S
{
// ...
}
}