这是我参与8月更文挑战的第1天,活动详情查看:8月更文挑战
C# 10 的更新内容很多,并且对类型系统做了不小的改动,解决了非常多现有的痛点。下面主要针对一些特性做说明
record struct
首先自然是 record struct,解决了 record 只能给 class 而不能给 struct 用的问题:
Copyrecord struct Point(int X, int Y);
复制代码
用 record 定义 struct 的好处其实有很多,例如你无需重写 GetHashCode
和 Equals
之类的方法了。
sealed record ToString
方法
之前 record 的 ToString 是不能修饰为 sealed
的,因此如果你继承了一个 record,相应的 ToString 行为也会被改变,因此这是个虚方法。
但是现在你可以把 record 里的 ToString 方法标记成 sealed
,这样你的 ToString
方法就不会被重写了。
struct 无参构造函数
一直以来 struct 不支持无参构造函数,现在支持了:
Copystruct Foo
{
public int X;
public Foo() { X = 1; }
}
复制代码
但是使用的时候就要注意了,因为无参构造函数的存在使得 new struct()
和 default(struct)
的语义不一样了,例如 new Foo().X == default(Foo).X
在上面这个例子中将会得出 false
。
匿名对象的 with
可以用 with 来根据已有的匿名对象创建新的匿名对象了:
Copyvar x = new { A = 1, B = 2 };
var y = x with { A = 3 };
复制代码
这里 y.A
将会是 3 。
全局的 using
利用全局 using 可以给整个项目启用 usings,不再需要每个文件都写一份。比如你可以创建一个 Import.cs,然后里面写:
Copyusing System;
using i32 = System.Int32;
复制代码
然后你整个项目都无需再 using System
,并且可以用 i32
了。
文件范围的 namespace
这个比较简单,以前写 namespace 还得带一层大括号,以后如果一个文件里只有一个 namespace 的话,那直接在最上面这样写就行了:
Copynamespace MyNamespace;
复制代码
常量字符串插值
你可以给 const string 使用字符串插值了,非常方便:
Copyconst string x = "hello";
const string y = $"{x}, world!";
复制代码
lambda 改进
这个改进可以说是非常大,我分多点介绍。
1. 支持 attributes
lambda 可以带 attribute 了:
Copyf = [Foo] (x) => x; // 给 lambda 设置
f = [return: Foo] (x) => x; // 给 lambda 返回值设置
f = ([Foo] x) => x; // 给 lambda 参数设置
复制代码
2. 支持指定返回值类型
此前 C# 的 lambda 返回值类型靠推导,C# 10 开始允许在参数列表最前面显示指定 lambda 类型了:
Copyf = int () => 4;
复制代码
3. 支持 ref 、in 、out 等修饰
Copyf = ref int (ref int x) => ref x; // 返回一个参数的引用
复制代码
4. 头等函数
函数可以隐式转换到 delegate,于是函数上升至头等函数:
Copyvoid Foo() { Console.WriteLine("hello"); }
var x = Foo;
x(); // hello
复制代码
5. 自然委托类型
lambda 现在会自动创建自然委托类型,于是不再需要写出类型了。
Copyvar f = () => 1; // Func<int>
var g = string (int x, string y) => $"{y}{x}"; // Func<int, string, string>
var h = "test".GetHashCode; // Func<int>
复制代码
CallerArgumentExpression
现在,CallerArgumentExpression
这个 attribute 终于有用了。借助这个 attribute,编译器会自动填充调用参数的表达式字符串,例如:
Copyvoid Foo(int value, [CallerArgumentExpression("value")] string? expression = null)
{
Console.WriteLine(expression + " = " + value);
}
复制代码
当你调用 Foo(4 + 5)
时,会输出 4 + 5 = 9
。这对测试框架极其有用,因为你可以输出 assert 的原表达式了:
Copystatic void Assert(bool value, [CallerArgumentExpression("value")] string? expr = null)
{
if (!value) throw new AssertFailureException(expr);
}
复制代码
tuple 支持混合定义和使用
比如:
Copyint y = 0;
(var x, y, var z) = (1, 2, 3);
复制代码
于是 y 就变成 2 了,同时还创建了两个变量 x 和 z,分别是 1 和 3 。