.NET 6 和 Visual Studio 2022 已于11月8日正式发布。内容很多,有兴趣可以自行了解,本文主要简单介绍一下 C# 10 的新功能。
本来我是把 DevBlog 全文翻译了的,但数据库丢了(悲),所以写个速览补上。
概览
- 全局 using
- 隐式 using
- 文件范围的命名空间
- lambda 和方法组的自然类型
- lambda 的返回类型
- lambda 上的特性
- 结构的无参构造函数
- 结构记录
- 记录的密封
ToString()
- 用于结构和匿名类型的
with
表达式 - 内插字符串处理程序
- 常量内插字符串
- 解构时混合声明与赋值
- 改进的赋值明确性推断
- 扩展的属性模式
- 调用方表达式
- 还在预览的最重要的功能
using 指令
using,每个文件都有,内容还大同小异,不如简化一下:
全局 using
在任何一个合法的 using 指令之前,加上一个 global 关键字,就可以进行 global using
。它的作用就是让这条 using 对整个项目生效。
注意,是任何一个。using A = B;
和 using static
也是可以的,把 using 放在哪个文件也没区别。所以 C# 也能实现全局函数了?
隐式 using
不同的项目类型会自带一些不同的全局 using,可以在项目文件里启用。因为不用你手写,所以叫隐式 using。
但实际上你想写也是可以写的,项目文件里加一行就行。同理,想删也一样。
文件范围的命名空间
namespace
现在可以单独拿出来用了。写上以后,效果就是为所在文件中的所有类型指定命名空间。
基本就是可以少一个缩进。
lambda 表达式和方法组
C# 最近几个版本都加了一些方便 FP 的功能,这次也不例外:
lambda 的自然类型
以前如果使用 lambda,必须有一个明确的目标委托类型。所以像 var
或者把 lambda 当成 object 之类的场景就显得很啰嗦。C# 10 开始,如果能推断出来,lambda 会有一个默认的类型。这个默认类型会尽量是 Action<...>
或者 Func<...>
;如果不行,编译器会生成一个匿名委托类型。表达式树也是支持的。
方法组的自然类型
只有一个重载的方法组和 lambda 有什么区别呢?所以它们也有自然类型,和 lambda 类似。
lambda 的返回类型
为了减少没法推断类型的情况,现在 lambda 可以指定返回类型。把返回类型放在 lambda 的参数列表之前即可。为了避免迷惑,参数列表的括号不能省略。
lambda 上的特性
lambda 编译出来就是方法,当然可以有特性。以及现在静态代码分析也很依赖特性,所以 C# 10 允许在 lambda 上加特性了。语法就是在 lambda 前面加中括号。
结构
结构一直以来功能都比类少很多,导致很多时候没法利用它提高性能。C# 10 希望可以减轻一下这种情况:
无参构造函数和字段初始化器
C# 10 之前结构的无参构造函数是自动生成的,而且如果你尝试自己写一个就会报错。现在就不会了,你不仅可以写无参构造函数,还可以给字段赋默认值。
但是吧,性能不可能丢,CLR 更不会改。只有用 new
创建的结构才会调用你的构造函数;用 default
或者数组里创建的还是会给所有字段赋默认值。
记录结构
原来的记录只能是类,现在可以是结构了。记录结构用 record struct
声明,原来的记录类现在也可以用 record class
声明。
记录结构会自动实现 IEquatable<T>
和 ==
运算符,替换掉自带的反射实现。记录的 ToString()
重写也是有的。
像记录类一样,记录结构也可以用位置参数定义。语法我没发现什么区别。
但是!自动生成的属性是可变的,因为这样可以更方便地替换匿名元组类型。给记录结构或者某个参数加上 readonly
可以让它不可变。
记录类中密封的 ToString()
现在可以给记录类的 ToString()
方法加上 sealed
,阻止编译器为派生记录生成重写。
用于结构和匿名类型的 with
表达式
就是标题说的这个东西现在有了。
内插字符串
C# 团队说当时加这个功能的时候就觉得不够好,然后现在终于能改了?
内插字符串处理程序
目前,内插字符串都会被编译成 string.Format
,不仅造成性能问题,还限制了各种花里胡哨的操作。C# 10 为库提供了一种模式,可以“接管”对内插字符串的处理。
比如,StringBuilder
就新增了一个 Append(ref StringBuilder.AppendInterpolatedStringHandler handler)
重载,在传入内插字符串时优先级更高,可以提高性能。一般来说,看到和这个长得差不多的 API,就是在用这个模式了。
另外两个例子:Debug.Assert
在条件为真时不会计算后面的内插字符串;String.Create()
可以为内插字符串里面的参数指定 IFormatProvider
。
常量内插字符串
如果是把常量字符串内插进字符串,现在生成的字符串也可以当常量用了。但如果内插的是个数字常量就不行,因为不同地区数字显示方式不同,只能运行时计算。
其他改进
混合解构
以前,解构之后要不然都赋值给已有变量,要不然都声明为新的。现在可以混合这两者,既有已有的又有新的。不知道为什么,这让我想到了 Go 的错误处理(。
明确赋值
C# 中,可以先声明变量再赋值。但在使用值之前必须确保变量有值,不管中间是有分支、循环还是其他什么结构。
编译器如果分析出变量有可能在使用时没有值,就会报错。C# 10 降低了误报率。
扩展的属性模式
现在,在模式匹配中,嵌套的属性模式可以直接通过用一个 .
连接多个属性名称代替,简化代码,方便阅读。
调用方表达式
和之前已有的调用方信息特性一个原理,新增了一个 CallerArgumentExpressionAttribute
,可以获取调用方在某个参数位置填了什么表达式,便于调试和抛异常。
结语
最重磅的功能,接口的静态抽象成员,估计是赶不上发布了,需要再预览一段时间,那我也就不介绍了,正式出来再说吧。
我觉得其实现在不用着急,等都预览完了再更也不迟。
本文主要还是按照这篇 DevBlog 写的,里面更详细,而且有很多链接,想深入了解可以看一看。
文章评论