查看原文
其他

Build 2020上公布的C# 9.0新特性

DotNet 2021-09-23

(给DotNet加星标,提升.Net技能

转自:MarkKang
cnblogs.com/markkang/archive/2020/05/22/12928528.html

在微软的Build 2020开发者大会中,微软就正在成形的C#9.0的一些即将添加的主要特性进行了说明。


一、init属性访问器


对象初始化方式对于创建对象来说是一种非常灵活和可读的格式,特别是对树状嵌入型对象的创建。简单的例如


new Person
{
FirstName = "Scott",
LastName = "Hunter"
}


原有的要进行对象初始化,我们必须要做就是写一些属性,并且在构造函数的初次调用中,通过给属性的setter赋值来实现。


public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}



二、init属性访问器和只读字段


因为init访问器只能在初始化时被调用,原来只能在构造函数里进行初始化的只读字段,现在可以在属性中进行初始化,不用再在构造函数进行初始化。省略了构造函数。


public class Person
{
private readonly string firstName;
private readonly string lastName;
public string FirstName
{
get => firstName;
init => firstName = (value ?? throw new ArgumentNullException(nameof(FirstName)));
}
public string LastName
{
get => lastName;
init => lastName = (value ?? throw new ArgumentNullException(nameof(LastName)));
}
}


三、Records



四、With表达式



var otherPerson = person with { LastName = "Hanselman" };



五、基于值的相等



如果你不喜欢默认Equals重写的字段与字段比较行为,你可以进行重写。你只需要认真理解基于值的相等时如何在records中工作原理,特别是涉及到继承的时候,后面我们会提到。


与基于值的Equals一起的,还伴有基于值的GetHashCode()的重写。


六、data成员



这个使得record声明看起来优美而清晰直观。如果你真的需要一个私有字段,你可以显式添加private修饰符。


private string firstName;

  

七、Positional records


 在record是可以指定构造函数和解构函数(注意不是析构函数)的。


public data class Person
{
string FirstName;
string LastName;
public Person(string firstName, string lastName)
=> (FirstName, LastName) = (firstName, lastName);
public void Deconstruct(out string firstName, out string lastName)
=> (firstName, lastName) = (FirstName, LastName);
}


也可以用更精简的语法表达上面同样的内容。


public data class Person(string FirstName, string LastName);


该方式声明了公开的带有初始化自动属性、构造函数和解构函数,和第6条第一段代码带有大括号的声明方式是不同的。现在你就可以写如下代码:


var person = new Person("Scott", "Hunter"); // positional construction
var (f, l) = person; // positional deconstruction



九、with表达式和继承


众所周知,基于值相等和非破坏性变化值的方式在和继承纠结在一起时,非常具有挑战性。下来,我们添加一个继承的record类Student来说明我们的例子:


public data class Person { string FirstName; string LastName; }
public data class Student : Person { int ID; }


下来,我们通过创建一个Student,但是把他存放到Person变量中,来说明with表达式的使用:


Person person = new Student { FirstName = "Scott", LastName = "Hunter", ID = GetNewId() };
otherPerson = person with { LastName = "Hanselman" };



然而,相等有着一个格外的挑战,就是如果比较两个不同类型的Person会怎么样?我们不能让他们中一个决定应用哪一个相等性:相等应该是语义的,所以不管两个对象中哪个先来,解构应是相同的。换句话说,他们必须在被应用的相等性达成一致。


这个问题的例子如下:


Person person1 = new Person { FirstName = "Scott", LastName = "Hunter" };
Person person2 = new Student { FirstName = "Scott", LastName = "Hunter", ID = GetNewId() };



using System;
class Program
{
static void Main()
{
Console.WriteLine("Hello World!");
}
}


这个对于初学者是无法抗拒,但是这使得代码凌乱,大量堆积,并且增加了缩进层级。在C#9.0中,你可以选择在顶级用如下代码代替写你的主程序:


using System;
Console.WriteLine("Hello World!");



十二、增强的模式匹配


C#9.0添加了几个新的模式,如果要了解下面代码段的上下文,请参阅模式匹配教程:


public static decimal CalculateToll(object vehicle) =>
vehicle switch
{
...
DeliveryTruck t when t.GrossWeightClass > 5000 => 10.00m + 5.00m,
DeliveryTruck t when t.GrossWeightClass < 3000 => 10.00m - 2.00m,
DeliveryTruck _ => 10.00m,
_ => throw new ArgumentException("Not a known vehicle type", nameof(vehicle))
};



DeliveryTruck t when t.GrossWeightClass switch
{
> 5000 => 10.00m + 5.00m,
< 3000 => 10.00m - 2.00m,
_ => 10.00m,
},


这的 > 5000 和 < 3000是关系模式。


3、逻辑模式


最后,你可以用逻辑操作符and,or 和not将模式进行组合,来详细说明,以避免表达式操作符引起的混淆。例如,上面嵌入的switch可以按照升序排序,如下:


DeliveryTruck t when t.GrossWeightClass switch
{
< 3000 => 10.00m - 2.00m,
>= 3000 and <= 5000 => 10.00m,
> 5000 => 10.00m + 5.00m,
},


中间的case使用了and 来组合两个关系模式形成了一个表达区间的模式。


not模式的公同使用是它将会被用在null常量模式上,就像not null。例如我们要根据是否为空来分割一个未知的case处理代码段:


not null => throw new ArgumentException($"Not a known vehicle type: {vehicle}", nameof(vehicle)),

null => throw new ArgumentNullException(nameof(vehicle))


not将在包含了is表达式的if条件语句使用也会很方便,并且会取代笨重的双括号:


if (!(e is Customer)) { ... }


你可以这样写:


if (e is not Customer) { ... }


十三、增强的目标类型化



Point p = new (3, 5);


2、目标类型化的??和?:


一些时候,条件表达式??和?:在分支中没有明显的共享类型。现有这种情况会失败,但是在C#9.0中,如果各分支可以转换 为目标类型,这种情况时允许的。


Person person = student ?? customer; // Shared base type
int? result = b ? 0 : null; // nullable value type


十四、支持协变的返回值


一些时候,在子类的一个重写方法中返回一个更具体的、且不同于父类方法定义的返回类型更为有用,C# 9.0对这种情况提供了支持。


abstract class Animal
{
public abstract Food GetFood();
...
}
class Tiger : Animal
{
public override Meat GetFood() => ...;
}


推荐阅读  点击标题可跳转
C# 9.0特性预览:空参数校验C# 9.0新特性预览:型推导的newC# 9.0新特性:代码生成器、编译时反射


看完本文有收获?请转发分享给更多人

关注「DotNet」加星标,提升.Net技能 

好文章,我在看❤️

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存