一、只初始化 setter (Init Only Setters)
背景
在此之前,我们创建实体类的时候,都期望属性只读不允许从外部修改,会将属性的 setter 设为私有或者干脆不设置 setter,例如:
public class Person
{
public string Name { get; private set; }
// OR
//public string Name { get; }
}
再添加一个拥有全部属性作为签名的构造方法:
...
public Person(string name)
{
this.Name = name;
}
...
这样做虽然可以达到目的,但是带来了两个问题 1.假如属性增多,会带来工作量的成倍增加,以及不易维护 2.无法使用对象初始化器(object initializers)
var person = new Person
{
Name = "Rwing" // Compile Error
};
在这个情况下,init 关键字应运而生了。
语法
语法很简单,只需要将属性中的 set 关键字替换为 init 即可:
public string Name { get; init; }
复制
以上代码会被大致翻译为:
private readonly string _name;
public string Name
{
get { return _name; }
set { _name = value;}
}
可以看到,与 set 的区别是,init 会为背后的字段添加 readonly 关键字。 这样我们就可以去掉一堆属性的构造方法转而使用对象初始化器了,并且达到了不可变的目的。
var person = new Person
{
Name = "Rwing"
};
// 初始化后无法再次修改
person.Name = "Foo"; // Error: Name is not settable
二、记录(record)
record 是介于类与结构体之间。
1、我们往往会创建一些简单的实体,它们仅仅拥有一些简单的属性,可能还有几个简单的方法,比如DTO等等,但是这样的简单实体往往又很有用,我们可能会遇到一些情况:
比如想要克隆一个新的实体而不是简单的引用传递
比如想要简单的比较属性值是否都一致,
比如在输出,我们希望得到内部数据结构而不是简单的甩给我们一个类型名称。
这些使用record刚好解决。
public record Person
{
public string FirstName { get; init; }
public string LastName { get; init; }
}
2、非破坏性变化:with
使用不可变数据(data
)时,一种常见的模式是从现有的值中创建新值来表示新状态。例如,如果我们的 person
要更改他们的 LastName
,我们会将其表示为一个新对象,该对象是旧对象的副本,只是有不同的 LastName
。这种技巧通常被称之为非破坏性突变(non-destructive mutation)。记录(record
)不是代表 person
在一段时间内的 状态,而是代表 person
在给定时间点的 状态。
为了帮助实现这种编程风格,记录(record
)允许使用一种新的表达式 —— with
表达式:
var otherPerson = person with { LastName = "Hanselman" };
三、顶级程序(Top-level programs)
// old: 方式
using System;
class Program
{
static void Main()
{
Console.WriteLine("Hello World!");
}
}
// new
using System;
Console.WriteLine("Hello World!");
四、模式匹配
-
声明模式
- 简单声明
object greeting = "Hello, World!"; if (greeting is string message) //匹配加声明 (message是声明的变量) Console.WriteLine(message.ToLower());
- Swith声明
Shape s = new Circle(); switch (s) //switch 匹配类型并声明 { case Circle cirle: break; case Rectangle rectangle: break; } abstract class Shape {} class Circle : Shape { } class Rectangle : Shape { }
- 简单声明
-
Var 模式
old:
bool IsValid(Person person)
{
return person.Age > 20 && person.Age < 30;
}
new:
bool IsValid(Person person) //当行方法简写,匹配var age作为变量参与接下来计算
=> person.Age is var age && age > 20 && age < 30;
-
常量模式
1、 object o = 20; if (o is 8){ }//相当于 o is i && i == 8 2、 double d = double.NaN; if (d == double.NaN) { } //false if (double.IsNaN(d)) { } //true if (d is double.NaN) { } //true
-
对位模式
using System.Text.RegularExpressions; class Program { static void Main() { var person = new Person { Name = "Kang", Age = 30, Gender = Gender.male }; if(person is ("Kang", 30)) //和Deconstruct方法参数对应 { Console.WriteLine("matche"); } } } class Person { public string Name { get; set; } public int Age { get; set; } public Gender Gender { get; set; } // 要使用对位模式必须写这个方法 public void Deconstruct(out string name, out int age) { // 将输出参数赋值 name = Name; age = Age; } } enum Gender { male, female }
-
解构模式
using System.Text.RegularExpressions; class Program { static void Main() { var person = new Person { Name = "Kang", Age = 30, Gender = Gender.male, Chinese=85, English=90, Match=60 }; if(person is var (name, avg) && avg >=60 ) { Console.WriteLine(name); } } } class Person { public string Name { get; set; } public int Age { get; set; } public Gender Gender { get; set; } public int Chinese { get; set; } public int English { get; set; } public int Match { get; set; } } enum Gender { male, female } static class PersonExtensions { public static void Deconstruct(this Person @this, out string name, out double avg) { name = @this.Name; avg = (@this.Chinese + @this.Match + @this.English) / 3D; } }
-
弃元模式
用法一、 var person = new Person { Name = "Kang", Age = 30, Gender = Gender.male, Chinese=85, English=90, Match=60 }; // 这里的下划线_就是弃元模式,我们可能用不到name。 if(person is var (_, avg) && avg >=60 ) { } 用法二、 _ = str ?? throw new Exception("str is null"); //判断str是空抛出异常
-
属性模式
示例一: var person = new Person { Name = "Kang", Age = 30}; // 这就是属性模式,不用写Deconstruct()方法了 if(person is { Name: "Kang" }) {} 示例二: if(person is {}) // 等价于 person != null {} 示例三: 语法:表达式 is 类型模式 (对位模式) {属性模式} 声明模式 if(person is Person("Kang", _) {Age: 18} converted) {} 示例四: 递归模式 if(person is { Name: { Length: 6 }, Friend: { Name: "Tom" } friend //Friend是person类型,递归判断 } ){}
-
关系模式
if(person is { Name: "Kang", Age: >= 18}){} //等价于 if(person.Name=="Kang" && person.Age >= 18){}
-
逻辑模式
string s = "123"; if(s is null or {Length : 0}){} //or作为逻辑判断,字符串为空或者长度等于0
-
拓展模式
常规属性匹配 if(person is { Friend: { Age >= 18 } } ){} 拓展模式:当对象只使用一个属性时,可以用点 if(person.Friend.Age >=18){}
-
列表模式
int[] arr = { 1, 2, 3 }; // 列表用[]表示,每个元素进行匹配, // 后面的..两个点表示这个列表后面可能有或者没有元素了 if(arr is [>=10 and <=20, _, not 42, ..]){}
-
分片模式
int[] arr = {1, 2, 3}; if( arr is [ >=10, _, not 42]){} //分别对三个数字进行判断