C#9.0新特性
目标类型推导
在这之前我们创建一个对象
Point p=new Point(x,y);
9.0中,进行了优化:
Point p=new (x,y);
Point[] ps = { new (1, 2), new (5, 2), new (5, -3), new (1, -3) };
//感觉跟var的使用类似
var p =new Point(x,y);
目标操作符
C# 9 也增加了操作符 ?? 和 ?: 的目标类型推导支持。之前这两个操作符必须要求两边的操作对象都是相同的类型,否则会编译报错。而在 C# 9 中,只要目标类型是操作对象共同的基类就不再会编译报错了
// Student 和 Customer 拥有共同的父类 Person
Person person = (Person)(student ?? customer); // C# 9 之前
Person person = student ?? customer; // C# 9
// 可空类型,0 和 null 都可以隐式转换为 int? 类型
int? result = b ? 0 : (int?)null; // C# 9 之前
int? result = b ? 0 : null; // C# 9
init类型
这是一个仅可初始化的属性,只能在类的构造函数中初始化一次,之后只读不可修改。
public class Foo
{
public string PropA { get; init; }
public string PropB { get; init; }
}
var foo= new Foo{ PropA = "C", PropB = "CC" }; // OK
foo.PropA = "ccc"; // ERROR!
record类型
如果是将某个属性设置为仅可读,可以像上面的方式去实现,那么如果是想要整个对象都是可读的话,那可以使用record记录关键字,使整个对象不可变,记录不是针对某个时间段的,而是在某个时间点的值。
public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
with表达式
with表达式,with 表达式的工作原理是将旧对象的完整状态实际地复制到一个新对象中,然后根据对象初始化器对其进行改变。
记录(record)隐式定义了一个受保护的(protected)“复制构造函数”——一个接受现有记录对象并逐字段将其复制到新记录对象的构造函数:
protected Person(Person original)
{
//拷贝所有字段
}
with 表达式会调用“复制构造函数”,然后在上面应用对象初始化器来相应地变更属性。
下面的例子是将FirstName修改,其他值不变,然后赋值给brother。
var person = new Person("Bill", "Wagner");
Person brother = person with { FirstName = "Paul" };
位置记录
有时候为了初始化更方便,可以定义构造函数来给属性赋值,初始化时只需要把属性值按顺序传给构造函数即可,这个操作称为定位构造(Positional Construction)。同样,也可以使用解构函数(Deconstructor)来实现属性的解构,即按照解构函数的参数顺序从对象中提取属性的值,被称为定位解构(Positional Deconstructor)。实现了定位构造或定位解构的记录称为定位记录(Positional Record)。
public record 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 record Person(string FirstName, string LastName);
编译器为位置记录生成 Deconstruct 方法。Deconstruct 方法的参数与记录类型中所有公共属性的名称匹配。Deconstruct 方法可用于将记录析构为其组件属性:
//构造定位
var person = new Person("Bill", "Wagner");
//解构定位
var (first, last) = person;
Console.WriteLine(first);
Console.WriteLine(last);
新类型
这次引入一组新类型(nint,nuint,nfloat等)该特性允许声明一个32位或64位的数据类型,这取决于操作系统的平台类型。
其中,nint 在32位系统占4个字节,在64位占8个字节
这个类型与InPtr相似,用法也是一样,可以代替InPtr使用。
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetParent")]
public extern static IntPtr SetParent(IntPtr childPtr, IntPtr parentPtr);
[System.Runtime.InteropServices.DllImport("user32.dll", EntryPoint = "SetParent")]
public extern static nint SetParent(nint childPtr, nint parentPtr);
模式匹配
参考匹配模式文中的片段代码
public class Car
{
public int Passengers { get; set; }
}
public class DeliveryTruck
{
public int GrossWeightClass { get; set; }
}
public class Taxi
{
public int Fares { get; set; }
}
public class Bus
{
public int Capacity { get; set; }
public int Riders { get; set; }
}
//C# 7.0的匹配模式
public decimal CalculateToll(object vehicle) =>
vehicle switch
};
简单类型模式(Simple type patterns)
目前,类型模式需要在类型匹配时声明一个标识符——即使该标识符是一个弃元 _,如上面的 DeliveryTruck _ 所示。
Car => 2.00m - 1.0m, //省略c
DeliveryTruck => 10.00m,//去掉弃元_
关系模式(Relational patterns)
C# 9.0 引入了与关系运算符 <、<= 等相对应的模式。现在可以将上述模式的 DeliveryTruck 部分编写为嵌套的 switch 表达式:
//c#7.0X写法
DeliveryTruck t when (t.GrossWeightClass > 5000) => 10.00m + 5.00m,
DeliveryTruck t when (t.GrossWeightClass < 3000) => 10.00m - 2.00m,
DeliveryTruck _ => 10.00m,
//C# 9.0改进
DeliveryTruck t when t.GrossWeightClass switch
{
5000 => 10.00m + 5.00m,
< 3000 => 10.00m - 2.00m,
_ => 10.00m,
}
这里的 > 5000 和 < 3000 是关系模式。
逻辑模式(Logical patterns)
最后,可以将模式与逻辑运算符 and、or 和 not 组合起来,避免与表达式中使用的运算符混淆。
if (!(e is Customer)) { ... }
if (e is not Customer) { ... }
string userInput = Console.ReadKey();
// 旧的方式
if (userInput== 'Y' || userInput== 'y')
{
Console.WriteLine("Do something.");
}
// 新的方式
if (userInput is 'Y' or 'y')
{
Console.WriteLine("Do something.");
}
例如,上面嵌套的switch的示例可以按如下升序排列:
DeliveryTruck t when t.GrossWeightClass switch
{
< 3000 => 10.00m - 2.00m,
= 3000 and <= 5000 => 10.00m,
5000 => 10.00m + 5.00m,
},
顶层模式
using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
借助顶层模式,可以将代码简化成以下:
using System;
Console.WriteLine("Hello World!");
协变的返回值
派生类中的方法重写具有一个比基类型中的声明更具体(更明确)的返回类型
abstract class Animal
{
public abstract Food GetFood();
...
}
class Tiger : Animal
{
public override Meat GetFood() => ...;
}