Build 2018大会:C#的未来

2018 年 5 月 28 日

看新闻很累?看技术新闻更累?试试下载 InfoQ 手机客户端,每天上下班路上听新闻,有趣还有料!

在 C#的未来特性清单上,排在第一位的是可空引用类型。我们第一次报道这个特性是在去年,这里我们简要的回顾一下:所有的引用变量、参数和字段默认都是非空的。然后,和值类型一样,如果你希望它们可以为空,你就必须在类型名上加一个问号(?)来显式说明。

这会是一项可选特性,目前的想法是,对于将升级到C# 8 的现有项目,可空引用类型特性是关闭的。对于新项目,微软倾向于默认开启这项特性。

警告会进一步分成潜在错误和表面警告。例如,如果p.MiddleName 是一个string?,那么下面这行代码会是一个表面警告:

复制代码
string middleName = p.MiddleName;

由于危险只会出现在值解引用的时候,所以这种对局部变量的赋值并不是一个真正的问题。因此,你可以在遗留代码上禁用这个警告,以减少误报数量。

同样,早于这项特性的库也不会触发警告,因为编译器不知道一个指定的参数是否应该视为可空的。

GitHub 上提供了可空引用类型的预览

Switch 表达式

Switch 块通常用于简单地返回单个值。在这个常见的场景中,其语法比实际完成的工作要复杂得多。考虑下下面这个使用模式匹配的例子:

复制代码
static string M(Person person)
{
switch (person)
{
case Professor p:
return $"Dr. {p.LastName}";
case Studen s:
return $"{s.FirstName} {s.LastName} ({s.Level})";
default:
return $"{person.FirstName} {person.LastName}";
}
}

在新的提案中,反复出现的 case 和 return 语句可以省掉,其结果是下面这种更新、更紧凑的语法:

复制代码
static string M(Person person)
{
return person switch
{
Professor p => $"Dr. {p.LastName}",
Student s => $"{s.FirstName} {s.LastName} ({s.Level})",
_ => $"{person.FirstName} {person.LastName}"
};
}

具体的语法还在讨论之中。例如,现在还不确定这个特性是要使用 => 还是: 分割模式表达式和返回值。

Property 模式匹配

当前,可以通过 when 子句执行 Property 层的模式匹配。

复制代码
case Person p when p.LastName == "Cambell" : return $"{p.FirstName} is not enrolled";

借助“Property 模式”,上述代码就简化为:

复制代码
case Person { LastName: "Cambell"} p : return $"{p.FirstName} is not enrolled";

变化很小,但却让代码更清晰,也去掉了一些冗余的变量名称。

从模式中提取 Property

更进一步,你可以在模式中声明变量:

复制代码
case Person { LastName: "Cambell", FirstName: var fn} p : return $"{fn} is not enrolled";

模式中的“解构器(Deconstructor)”

解构器用于把对象解构成其组成部分。它主要用于从元组多重赋值。在 C# 7.3 中,你还可以把解构器和模式匹配一起使用。

在下面的例子中,Person 类被解构成{FirstName, MiddleName, LastName}。由于我们不使用 MiddleName,所以我们使用一条下划线来跳过这个属性:

复制代码
case Person ( var fn, _, "Cambell" ) p : return $"{fn} is not enrolled";

递归模式

下一个模式,假设 Student 类解构成{FirstName, MiddleName, LastName, Professor}。我们可以同时解构 Student 对象及其子类 Professor 对象。

复制代码
case Student ( var fn, _, "Cambell", var (_, _, ln) ) p : return $"{fn} is enrolled in {ln}’s class";

在这行代码中,我们:

  1. 把 student.FirstName 提取到 fn
  2. 跳过 student.MiddleName
  3. 把 student.LastName 匹配成“Cambell”
  4. 跳过 student.Professor.FirstName 和 student.Professor.MiddleName
  5. 把 student.Professor.LastName 提取到 ln

GitHub 上提供了递归模式匹配的预览

索引表达式

索引表达式消除了在使用类数组数据结构时的大部分冗长(易出错)的语法。

帽子操作符(^)从列表尾部开始计数。例如,要获取字符串的最后一个值可以这样写:

复制代码
var lastCharacter = myString[myString.Length-1];

或者简单地写成:

复制代码
var lastCharacter = myString[^1];

该特性适用于计算值,如:

复制代码
Index nextIndex = ^(x + 1);
var nextChar = myString[nextIndex]

范围表达式

范围表达式和索引表达式密切相关,用(…)操作符表示。下面是一个简单的例子,获取字符串中的前三个字符:

复制代码
var s = myString.Substring[0..2];

范围表达式可以结合索引表达式一起使用。在下面这行代码中,我们跳过了第一个和最后一个字符。

复制代码
var s = myString.Substring(1..^1);

范围表达式也适用于 Span。

复制代码
Span<int> slice = myArray[48];

注意,第一个数值包含,第二个数值不包含。因此,变量 slice 包含元素 4、5、6、7。

要获取一个切片,包含从某个索引值往后的所有元素,有两种方法:

复制代码
Span<int> rest = myArray[8..];
Span<int> rest = myArray[8..^0];

类似地,你可以省略第一个索引:

复制代码
Span<int> firstFour = myArray[..4];

不用觉得奇怪,这个语法主要是受 Python 启发,主要的区别是 C#不能使用 -1 表示数组末尾索引,因为那在.NET 数组中已经有一个意思了。因此,我们使用 ^1 语法代替。

GitHub 上提供了索引和范围的预览

IAsyncDisposable

顾名思义,该接口允许对象暴露一个 DisposeAsync 方法。与此对应的是新语法“using async”,它和普通的 using 块一样,但增加了等待 dispose 调用的功能。

异步枚举

和 IEnumerable 类似,IAsyncEnumerable 让你可以枚举一个长度未知的有限列表。不过,配备的枚举器看上去有轻微的差别。它暴露了两个方法:

复制代码
Task<bool> WaitForNextAsync();
T TryGetNext(out bool success);

该接口有一个有趣的特性,就是允许你批量读取数据。你针对批次中的每一个数据项调用 TryGetNext 方法。当返回 success=false 时,你可以调用 WaitForNextAsync 获取一个新的批次。

这之所以重要,是因为大多数进入应用程序的数据要么是按批次,要么是从网络流入。当你调用 TryGetNext 时,数据大多数时候都已经是可用的。分配一个 Task 对象有点浪费,除非你用完了输入缓冲区中的数据,并且仍然希望能够异步等待更多数据。

在理想情况下,你不会经常直接使用这些接口。取而代之,微软希望你使用异步迭代器语法“foreach await”,我们去年预览过。它会根据需要处理同步或异步方法的调用。

默认接口方法

这个灵感来自Java、颇具争议的特性仍然被考虑加入C# 8 中。简单来说,它让你可以添加新方法及其实现,达到接口演化的目的。这样,新方法不会破坏向后兼容性。

记录

记录是一种快速创建不可变类的语法。我们第一次看到这个提案是在2014 年。当前的例子如下:

复制代码
class Person(string FirstName, string LastName);

从根本上说,它完全是一个由构造函数签名定义的类。对于一个不可变类,你想要的所有属性和方法都是自动生成的。

查看英文原文 Build 2018: The Future of C#

2018 年 5 月 28 日 15:352104
用户头像

发布了 1008 篇内容, 共 307.1 次阅读, 收获喜欢 272 次。

关注

评论

发布
暂无评论
发现更多内容

如何成为一名合格的 C/C++ 开发者?

张小方

c++ Linux 编程语言 架构设计 后端开发

我写了一本操作系统词典送给你

cxuan

操作系统 计算机

大型互联网应用系统使用技术方案和手段

读闲书自由和财务自由

池建强

读书 财务自由

奈学:数据湖有哪些缺点?

古月木易

数据湖

架构演化

满山李子

大型互联网架构与集群技术

xzm

嗨,兄弟,别担心,这年头谁还没有一点焦虑!

攀岩飞鱼

管理 程序员人生 成长 个人感想 程序员素养

第四章总结

ARTS 打卡 Week 05

teoking

奈学:数据湖有哪些缺点?

奈学教育

数据湖

架构师训练营第四周课后作业

竹森先生

极客时间 极客大学架构师训练营

谈谈架构和微服务<一>

Gabriel

架构 微服务 微服务架构 领域驱动设计 软件设计

典型的大型互联网应用系统

Z冰红茶

一文搞懂 Redis高性能之IO多路复用

flyer0126

redis io 多路复用 高性能

互联网架构学习总结

qihuajun

极客大学架构师训练营第四周学习总结

竹森先生

极客大学 极客大学架构师训练营

架构师训练营第 4 周——学习总结

在野

极客大学架构师训练营

深入理解Kubernetes的Service:回归本源的场景需求

韩超

Kubernetes 微服务 服务

如何进行高效学习

淡蓝色

深度思考 方法论 感悟 随笔杂谈

轻松上手promise原理(2):then的简单实现

前端小帅

互联网架构作业

qihuajun

SpringBatch系列之Remote-Chunking

稻草鸟人

大数据 Spring Boot SpringBatch 批量任务

奈学:数据湖和数据仓库的区别有哪些?

奈学教育

数据仓库 数据湖

游戏夜读 | 游戏关卡设计师

game1night

【week04】作业

chengjing

动态规划算法重点在于找上一个的公式,Google Code Review,John 易筋 ARTS 打卡 Week 06

John(易筋)

ARTS 打卡计划

奈学:数据湖和数据仓库的区别有哪些?

古月木易

数据仓库 数据湖

架构师训练营第 4 周作业

在野

极客大学架构师训练营

消息队列(一)为什么要使用消息队列?

奈何花开

Java MQ 消息队列

MySQL 实战 45 讲笔记(2)-查询优化

王传义

MySQL

Build 2018大会:C#的未来-InfoQ