C# 9.0 添加和增强的功能

一、记录(record)

C# 9.0 引入了记录类型。 可使用 record 关键字定义一个引用类型,以最简的方式创建不可变类型。这种类型是线程安全的,不需要进行线程同步,非常适合并行计算的数据共享。

它减少了更新对象会引起各种bug的风险,更为安全。System.DateTime 和 string 也是不可变类型非常经典的代表。

与类不同的是,它是基于值相等而不是唯一的标识符--对象的引用。

通过使用位置参数或标准属性语法,可以创建具有不可变属性的记录类型,整个对象都是不可变的,且行为像一个值。

优点:

1、在构造不可变的数据结构时,它的语法简单易用

2、record 为引用类型,不用像值类型在传递时需要内存分配,还可整体拷贝

3、构造函数和结构函数为一体的、简化的位置记录;

4、有力的相等性支持 —— 重写了 Equals(object), IEquatable, 和 GetHashCode() 这些基本方法。

record 类型可以定义为可变的,也可以是不可变的。

// 没有 set 访问器,创建后不可更改,叫不可变类型
public record Person
{
    // 要支持用对象初始化器进行初始化,则在属性中使用 init 关键字
    // 或者以构造函数的方式
    public string? FirstName { get; init; }
    public string? LastName { get; init; }
}
// 可变类型的 record
// 因为有 set 访问器,所以它支持用对象初始化器进行初始化
public record Person
{
    public string? FirstName { get; set; }
    public string? LastName { get; set; }
}

记录(record)和类一样,在面向对象方面,支持继承,多态等所有特性。除过前面提到的 record 专有的特性,其他语法写法跟类也是一样。同其他类型一样,record 的基类依然是 object。

注意:

1、记录只能从记录继承,不能从类继承,也不能被任何类继承

2、record 不能定义为 static 的,但是可以有 static 成员。

从本质上来讲,record 仍然是一个类,但是关键字 record 赋予这个类额外的几个像值的行为:

1、基于值相等性的比较方法,如 Equals,==,!=,EqualityContract 等;

2、重写 GetHashCode();

3、拷贝和克隆成员;

4、PrintMembers 和 ToString() 方法。

应用场景:

1、用于 web api 返回的数据,通常作为一种一次性的传输型数据,不需要是可变的,因此适合使用 record;

2、作为不可变数据类型 record 对于并行计算和多线程之间的数据共享非常适合,安全可靠

3、record 本身的不可变性和 ToString 的数据内容的输出,不需要人工编写很多代码,就适合进行日志处理;

4、其他涉及到有大量基于值类型比较和复制的场景,也是 record 的常用的使用场景。

with 表达式

当使用不可变的数据时,一个常见的模式是从现存的值创建新值来呈现一个新状态。

例如,如果 Person 打算改变他的姓氏(last name),我们就需要通过拷贝原来数据,并赋予一个不同的 last name 值来呈现一个新 Person。这种技术被称为非破坏性改变。作为描绘随时间变化的 person,record 呈现了一个特定时间的 person 的状态。

为了帮助进行这种类型的编程,针对 records 就提出了 with 表达式,用于拷贝原有对象,并对特定属性进行修改

// 修改特定属性后复制给新的 record
var person = new Person { FirstName = "Mads", LastName = "Nielsen" };
var otherPerson = person with { LastName = "Torgersen" };
// 只是进行拷贝,不需要修改属性,那么无须指定任何属性修改
Person clone = person with { };

with 表达式使用初始化语法来说明新对象在哪里与原有对象不同。with 表达式实际上是拷贝原来对象的整个状态值到新对象,然后根据对象初始化器来改变指定值。这意味着属性必须有 init 或者 set 访问器,才能用 with 表达式进行更改

注意:

1、with 表达式左边操作数必须为 record 类型;

2、record 的引用类型成员,在拷贝的时候,只是将所指实例的引用进行了拷贝。

二、仅限 Init 的资源库

从 C# 9.0 开始,可为属性和索引器创建 init 访问器,而不是 set 访问器。 调用方可使用属性初始化表达式语法在创建表达式中设置这些值,但构造完成后,这些属性将变为只读。

仅限 init 的资源库提供了一个窗口用来更改状态。 构造阶段结束时,该窗口关闭。 在完成所有初始化(包括属性初始化表达式和 with 表达式)之后,构造阶段实际上就结束了。

属性初始值设定项可明确哪个值正在设置哪个属性。 缺点是这些属性必须是可设置的。

可在编写的任何类型中声明仅限 init 的资源库。

例如,以下结构定义了天气观察结构:

// 以下结构定义了天气观察结构
public struct WeatherObservation
{
    public DateTime RecordedAt { get; init; }
    public decimal TemperatureInCelsius { get; init; }
    public decimal PressureInMillibars { get; init; }
    public override string ToString() =>
        $"At {RecordedAt:h:mm tt} on {RecordedAt:M/d/yyyy}: " +
        $"Temp = {TemperatureInCelsius}, with {PressureInMillibars} pressure";
}
// 调用方可使用属性初始化表达式语法来设置值,同时仍保留不变性
var now = new WeatherObservation 
{ 
    RecordedAt = DateTime.Now, 
    TemperatureInCelsius = 20, 
    PressureInMillibars = 998.0m 
};
//初始化后尝试更改观察值会导致编译器错误
// Error! CS8852.
now.TemperatureInCelsius = 18;

对于从派生类设置基类属性,仅限 init 的资源库很有用。这些设置器可在 with 表达式中使用。 可为定义的任何 classstruct 或 record 声明仅限 init 的资源库。

三、顶级语句

顶级语句,就是从应用程序中删除了不必要的流程。例如最基本的“HelloWorld!”:

using System;
namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
        }
    }
}
// 只有一行代码执行所有操作,借助顶级语句
// 可使用 using 指令和执行操作的一行替换所有样本
using System;
Console.WriteLine("Hello World!");
// 如果需要单行程序,可删除 using 指令,并使用完全限定的类型名称
System.Console.WriteLine("Hello World!");

应用程序中只有一个文件可使用顶级语句。

如果编译器在多个源文件中找到顶级语句,则是错误的。

如果将顶级语句与声明的程序入口点方法(通常为 Main 方法)结合使用,也会出现错误。

从某种意义上讲,可认为一个文件包含通常位于 Program 类的 Main 方法中的语句。

顶级语句可提供类似脚本的试验体验,这与 Jupy

  • 22
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值