一探即将到来的 C# 10
前言
本来因为懒不想写这篇文章,但是不少人表示有兴趣,于是最后决定还是写一下。
.NET 6 最近几个预览版一直都在开发体验(如 hot reload、linker 等)、平台支持(如 Android、iOS 等)、工具链(如 crossgen2、PGO 工具和 wasm 的 AOT 等)、JIT(如 LSRA、Jump threading、PGO 和 guarded devirtualization 以及使 struct 保持在寄存器上等)、GC(如 Regions 等)以及 BCL(如 TimeOnly、DateOnly 以及 Json DOM 等)方面做改进,然而却一直没有公布 C# 10 的任何内容,即使在 Build 2021 大会上也没有提及这方面内容。然而实际上不少特性的实现已经接近尾声了,那么让我们提前来看看 C# 10 可以为我们带来什么东西。
当然,不是所有下面列出的特性都一定会进入 C# 10,也可能会和本文有所出入,我在每一个特性后面加了一个百分比表示最终实装的可能性,仅供参考。
Backing Fields(60%)
相信不少人在编写属性的时候,因为自动属性不能满足自己的需求于是不得不改回手动实现属性,这个时候总是会想“如果能不用手动写字段的定义就好了”,现在这个梦想成真了:
private int myInt;
public int MyInt { get => myInt; set => myInt = value; }
C# 10 中新增了一个 field,当使用它时会自动为属性创建字段定义,不需要再手动定义字段了,因此也叫做半自动属性。
public int MyInt { get => field; set => field = value; }
Record Structs(100%)
Records 此前只支持 class,但是现在同样支持 struct 啦,于是你可以定义值类型的 record,避免不必要的堆内存分配:
record struct Point(int X, int Y);
with on Anonymous Objects(80%)
此前 with 只能配合 records 使用,但是现在它被扩展到了匿名对象上,你可以通过 with 来创建匿名对象的副本并且修改它的值啦:
var foo = new { A = 1, B = "test", C = 4.4 };
var bar = foo with { A = 3 };
Console.WriteLine((bar.A, bar.B, bar.C)); // (3, test, 4.4)
Global Usings(80%)
此前 using 语句的生效范围是单个文件的,如果你想使用一些 namespace,或者定义一系列的类型别名在整个项目内使用,那么你就需要这样:
using System.Linq;
using static System.Math;
using i32 = System.Int32;
using i64 = System.Int64;
然后在每个文件中重复一遍。但是现在不需要了,你可以定义全局的 using 了:
global using System.Linq;
global using static System.Math;
global using i32 = System.Int32;
global using i64 = System.Int64;
然后在整个项目中就都可以用了。
File Scoped Namespace(90%)
C# 10 开始你将能够在文件顶部指定该文件的 namespace,而不需要写一个 namespace 然后把其他代码都嵌套在大括号里面,毕竟绝大多数情况下,我们在写代码时一个文件里确实只会写一个 namespace,这样可以减少一层嵌套也是很不错的:
namespace MyProject;
class MyClass
{
// ...
}
如果采用这样的写法,每一个文件将只能声明一个 namespace。
Constant Interpolated String(100%)
顾名思义,常量字符串插值:
const string a = "foo";
const string b = $"{a}_bar"; // foo_bar
常量字符串插值将在编译时完成。
Lambda Improvements(100%)
C# 10 大幅度改进了 lambda,扩展了使用场景,并改进了一系列的推导,提出自然委托类型,还函数上升至 first-class。
支持 Attributes
f = [Foo] (x) => x; // 给 lambda 设置
f = [return: Foo] (x) => x; // 给 lambda 返回值设置
f = ([Foo] x) => x; // 给 lambda 参数设置
支持显示指定返回值类型
此前 C# 的 lambda 返回值类型靠推导,C# 10 开始允许在参数列表最前面显示指定 lambda 类型了:
f = int () => 4;
支持 ref 等修饰
f = ref int (ref int x) => ref x; // 返回一个参数的引用
First-class Functions
方法可以被隐式转换到 Delegate,使得函数上升至 first-class。
Delegate f = 1.GetHashCode; // Func<int>
object g = 2.ToString; // object(Func<string>)
var s = (int x) => x; // Func<int, int>
将函数作为变量,然后传给另一个函数的参数:
void Foo(Func<int> f)
{
Console.WriteLine(f());
}
int Bar()
{
return 5;
}
var baz = Bar;
Foo(baz);
Natural Delegate Types
lambda 现在会自动创建自然委托类型。
可以用 var 来创建委托了:
var f = () => 1; // Func<int>
var g = string (int x, string y) => $"{y}{x}"; // Func<int, string, string>
var g = "test".GetHashCode; // Func<int>
调用 lambdas
得益于上述改进,创建的类型明确的 lambda 可以直接调用了。
var zero = ((int x) => x)(0); // 0
Caller Expression Attribute(80%)
现在,CallerArgumentExpression 这个 attribute 终于有用了。借助这个 attribute,编译器会自动填充调用参数的表达式字符串,例如:
void Foo(int value, [CallerArgumentExpression("value")] string? expression = null)
{
Console.WriteLine(expression + " " + value);
}
当你这样调用时:
Foo(4 + 5);
会输出 4 + 5 = 9。这对测试极其有用,因为你可以输出 assert 的原表达式了:
static void Assert(bool value, [CallerArgumentExpression("value")] string? expr = null)
{
if (!value) throw new AssertFailureException(expr);
}
default 支持解构(100%)
default 现在支持解构了,因此可以给 tuples 直接赋值。
(int a, int b, int c) = default; // (0, 0, 0)
List Patterns(100%)
Pattern Matching 的最后一块版图:list patterns,终于补齐了。
void Foo(List<int> list)
{
switch (list)
{
case [4]:
Console.WriteLine("长度为 4");
break;
case { 1, 2, 3 }:
Console.WriteLine("元素是 1, 2, 3");
break;
case { 1, 2, ..var x, 5 }:
Console.WriteLine($"前两个元素是 1, 2,最后一个元素是 5,倒数第二个元素是 {x}");
break;
default:
Console.WriteLine("其他");
}
}
同样的,该 pattern 也是 recursive 的,因此你可以嵌套其他 patterns。
除了上述 switch statements 的用法,在 if 以及 switch expressions 等地方也同样可用,例如:
void Foo(List<int> list)
{
var result = list switch
{
[4] => ...,
{ 1, 2, 3 } => ...,
{ 1, 2, ..var x, 5 } => ...,
_ => ...
};
}
Abstract Static Member in Interfaces(100%)
C# 10 中,接口可以声明抽象静态成员了,.NET 的类型系统正式具备 virtual static dispatch 能力。
例如,你想定义一个可加而且有零的接口 IMonoid:
interface IMonoid<T> where T : IMonoid<T>
{
abstract static T Zero { get; }
abstract static T operator+(T l, T r);
}
然后可以对其进行实现,例如这里的 MyInt:
public class MyInt : IMonoid<MyInt>
{
public MyInt(int val) { Value = val; }
public static MyInt Zero { get; } = new MyInt(0);
public static MyInt operator+(MyInt l, MyInt r) => new MyInt(l.Value + r.Value);
public int Value { get; }
}
然后就能写出一个方法对 IMoniod<T> 进行求和了,这里为了方便写成扩展方法:
public static class IMonoidExtensions
{
public static T Sum<T>(this IEnumerable<T> t) where T : IMonoid<T>
{
var result = T.Zero;
foreach (var i in t) result += i;
return result;
}
}
最后调用:
List<MyInt> list = new() { new(1), new(2), new(3) };
Console.WriteLine(list.Sum().Value); // 6
这个特性同样也会对 .NET BCL 做出改进,会新增诸如 IAddable<T>、INumeric<T> 的接口,并为适用的已有类型实现。
总结
以上就是在 C# 10 的大部分新特性介绍了,虽然不保证最终效果和本文效果一致,但是也能看到一个大概的方向。
从 interface 的改进上我们可以看到一个好的预兆:.NET 终于开始动类型系统了。2008 年至今几乎没有变过的 CTS 显然逐渐不能适应语言发展的需要,而 .NET 团队也明确给出了信息表明要在 C# 11 前后对类型系统集中进行改进,现在只是一个开始,相信不久之后也将能看到 traits、union types、bottom types 和 HKT 等的实装。
一探即将到来的 C# 10的更多相关文章
- 准备:新V8即将到来,Node.js的性能正在改变
V8的Turbofan的性能特点将如何对我们优化的方式产生影响 审阅:来自V8团队的Franziska Hinkelmann和Benedikt Meurer. **更新:Node.js 8.3.0已经 ...
- 即将到来的Android N,将具备这些新特性
原文转自:http://www.leiphone.com/news/201602/pSRQAuAjMFJITqHe.html 原创 訾竣喆 即将到来的Android N,将具备这些新特 ...
- 5G即将到来,你还会购买4G手机吗?
科技在不断进步,通信技术也是如此,5G网络将于明年下半年开始测试部署,4G手机是否值得更换呢?三星上周发布了Galaxy Note 9智能手机,这也给消费者带来了一个难题:到底是现在花上1000美元将 ...
- 企业SAAS的春天,将以手机应用的形式,即将到来
派尔科技吴春福 *本文是派尔为什么要投身企业移动应用的内部分享文章: *我没有仔细核查资料,仅代表个人看法,思路也是在整理过程,逻辑未必很完整,看官将就着看. 企业SAAS,概念起源是N年前,先行者也 ...
- [翻译] 初看 ASP.NET Core 3.0 即将到来的变化
[翻译] 初看 ASP.NET Core 3.0 即将到来的变化 原文: A first look at changes coming in ASP.NET Core 3.0 在我们努力完成下一个 m ...
- AI-Info-Micron-Insight:5G、人工智能和即将到来的移动革命
ylbtech-AI-Info-Micron-Insight:5G.人工智能和即将到来的移动革命 1.返回顶部 1. 5G.人工智能和即将到来的移动革命 人们都说自己的手机“智能”,但究竟有多智能?凡 ...
- 从 HTTP/1 到 HTTP/2,以及即将到来的 HTTP/3
如今的生活中已经离不开互联网,智能家居.在线支付.网上购物都需要互联网的支持.互联网切切实实地给生活带来了诸多便利.有了互联网,我们可以呆在空调房里,一边刷着微博,一边等透心凉的西瓜送到手上,安安静静 ...
- 即将到来的“分布式云”(DPaaS):分布式计算+ DB +存储即服务
我在区块链会议上就即将到来的公共"分布式云"系统进行了讨论,该系统将主流的公共云平台(如AWS,Azure,Google Cloud,Heroku等)与区块链和P2P网络相结合,比 ...
- Android P正式版即将到来:后台应用保活、消息推送的真正噩梦
1.前言 对于广大Android开发者来说,Android O(即Android 8.0)还没玩热,Andriod P(即Andriod 9.0)又要来了. 下图上谷歌官方公布的Android P ...
随机推荐
- Day13_73_死锁
死锁 什么是死锁? - 是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去. 死锁出现的原因? (1) 因为系统资源不足. 如果系统资源充足 ...
- 【日志】MySQL中有多少种日志
redo 重做日志 作用:确保事务的持久性,防止在发生故障,脏页未写入磁盘.重启数据库会进行redo log执行重做,到达事务一致性 undo 回滚日志 作用:保证数据的原子性,记录事务发生之前的数据 ...
- SSDT表概念详解
SSDT 的全称是 System Services Descriptor Table,系统服务描述符表. 这个表就是一个把 Ring3 的 Win32 API 和 Ring0 的内核 API 联系起来 ...
- 编译Android 4.4源代码并烧录到Nexus4
环境准备: 基本环境:ubuntu-12.04-desktop-64bit(裸机或者Windows下虚拟机安装均可,14.04也可以) 其他要求:空闲磁盘空间100G以上,代码部分接近10G,内存越大 ...
- POJ2553 强连通出度为0的应用
题意: 给你一个有向图,然后问你有多少个满足要求的点,要求是: 这个点能走到的所有点都能走回这个点,找到所有的这样的点,然后排序输出. 思路: 可以直接一遍强连通缩点,所点之后 ...
- DVWA之DOM XSS(DOM型跨站脚本攻击)
目录 Low Medium High Impossible Low 源代码: <?php # No protections, anything goes ?> 从源代码可以看出,这里low ...
- Intel汇编语言程序设计学习-第六章 条件处理-中
6.3 条件跳转 6.3.1 条件结构 在IA-32指令集中没有高级的逻辑结构,但无论多么复杂的结构,都可以使用比较和跳转指令组合来实现.执行条件语句包括两个步骤:首先,使用CMP,AND,SUB ...
- Redis数据结构—链表与字典的结构
目录 Redis数据结构-链表与字典的结构 链表 Redis链表节点的结构 Redis链表的表示 Redis链表用在哪 字典 Redis字典结构总览 Redis字典结构分解 Redis字典的使用 Re ...
- 【python】Leetcode每日一题-寻找旋转排序数组中的最小元素
[python]Leetcode每日一题-寻找旋转排序数组中的最小元素 [题目描述] 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组.例如,原数组nums ...
- 1 cmd
打开cmd 在终端打开指定文件 按住alt+e打开我的电脑 补充: 1.windows和+组合快捷键 放大镜,(同理windows -) 然后在地址栏输入cmd,回车即可 常用命令 #盘符切换 ...