写点什么

C# 7 特性预览

  • 2016-04-06
  • 本文字数:3009 字

    阅读完需:约 10 分钟

在过去一年间,我们为读者展示了多个已考虑加入C# 7 中的特性。在最近发布的Visual Studio 15 预览版中,微软决定为用户展现这些特性,使其成为C# 7 最终发布的一部分。

元组值类型

.NET 提供了一个元组(Tuple)类型,但具体在C#中使用时却存在着各种各样的问题。由于元组类型是一个引用类型,因此在一些对于性能相当敏感的代码中,你很可能会避免因使用它而造成GC 的开销。同时,元组类型是不可变的,虽然这使跨线程共享变得更安全,但也意味着每次进行变更都必须分配一个新的对象。

为了应对这一问题,C# 7 将提供一个值类型的元组。这是一个可变类型,对那些重视性能的代码来说,这种方式将更为高效。同时,作为值类型,它在每次进行分配时都会生成一个拷贝,因此几乎没有产生多线程问题的风险。

你可以通过以下语法创建一个元组:

复制代码
var result = (5, 20);

你也可以选择对元组中的值进行命名,这一点并不是必须的,只是让代码具有更好的可读性。

复制代码
var result = (count: 5, sum: 20);

你可能会想,“很棒的特性,但我自己也能写得出来”。但下一个特性才是重头戏。

多返回值

在类C 风格的语言中,要在一个函数中返回两个值始终是一件麻烦事。你只能选择将结果封装成某种结构,或是使用输出参数。与许多函数式编程语言一样,C#选择了第一种方式为你提供这一特性:

复制代码
(int, int) Tally (IEnumerable<int> list)

可以看到,在这里使用泛用的元组有一个基本问题:我们将无从得知每个字段的作用。因此,C#选择通过一个编译器花招对结果进行命名:

复制代码
(int Count, int Sum) Tally (IEnumerable<int> list)

我们在此需要强调一点:C#并没有生成一个新的匿名类型,你所获得的仍旧是一个元组,但编译器将假设它的属性为 Count 和 Sum,而不是 Item1 和 Item2。所以,以下代码行的作用都是等价的:

复制代码
var result = Tally(list);
Console.WriteLine(result.Item1);
Console.WriteLine(result.Count);

请注意一点,我们现在还不具备多赋值语法,如果这种语法最终实现,那么它的用法可能是这样的:

复制代码
(count, sum) = Tally(list);

除了提供简单的功能性函数之外,多返回值的实用性还体现在异步代码的编写上,因为在 async 函数中是不允许使用 out 参数的。

模式匹配:改进的 Switch 语法块

VB 与函数式程序员对于 C#抱怨得最多的一点就是 C#中的 switch 语句功能十分有限。VB 开发者希望能够进行范围匹配,而习惯了 F#或 Haskell 的开发者则希望能够使用分解式的模式匹配。C#打算同时提供这两种特性。

在对类型进行模式匹配时,你可以创建一个变量以保存转型的结果。举例来说,在对一个 System.Object 使用 switch 语句时,你可以编写以下代码:

复制代码
case int x:

如果该对象是数值类型,则变量 x 将得以赋值。否则的话,程序将按从上至下的顺序检查下一个 case 语句块。如果你想更具体地进行匹配,还可以使用范围检查:

复制代码
case int x when x > 0:
case int y:

在这个示例中,如果该对象是正整数,则 x 代码块将被执行。如果对象是 0 或负整数,而 y 代码块将被执行。

如果需要检查 null 值,则只需使用以下语法:

复制代码
case null;

模式匹配:分解

目前为止,我们仅仅展示了某种对 VB 中已有的特性所做的增量式改进,而模式匹配真正的强大之处在于分解,它可以将某个对象完全拆开,考虑一下以下语法:

复制代码
if (person is Professor {Subject is var s, FirstName is "Scott"})

这段代码完成了两件事:

  1. 它创建了一个本地变量 s,将其赋值为 ((Professor)person).Subject。
  2. 它执行了一次相等性检查 ((Professor)person).FirstName == “Scott”。

如果将其用 C# 6 代码改写则是这样:

复制代码
var temp = person as Professor;
if (temp != null && temp.FirstName == "Scott")
{
var s = temp.Subject

在最终发布中,我们预计能够同时看到对 switch 语句块的这两种改进。

引用返回

对于大数据结构进行引用传递比起值传递要快得多,因为后者需要对整个结构进行拷贝。与之类似,返回一个大数据结构的引用一样能够提升速度。

在类似于 C 这样的语言中,可以通过指针返回某个结构的引用。这种方式会带来一个常见的问题,即指针所指向的内存可能会因为某种原因而已经被回收了。

C#通过使用引用的方式回避这一问题,引用本身是一个附加了规则的指针。最重要的一条规则是,你不能够返回某个本地变量的引用。如果你尝试这样做,那么该变量所引用的栈信息在函数返回时就已经变得不可访问了。

在微软的展示代码中,它所返回的引用指向一个数组中的某个结构。由于它实质上是指向数组中某个元素的指针,因此随后可以对数组本身进行修改。举例来说:

复制代码
var x = ref FirstElement(myArray)
x = 5; //MyArray[0] now equals 5

这一语法的用例是对性能高度敏感的代码,在大多数应用中都无需使用这一特性。

二进制字面值(Binary Literals)

此次发布还引入了一个小特性,即二进制字面值。这一语法只是一个简单的前缀而已,例如 5 可以表示为“0b0101”。这一特性的主要用例是设置基于 flag 的枚举,以及创建位掩码(bitmask),以用于与 C 风格语言的互操作。

本地函数

本地函数是指在另一个函数中所定义的函数。第一眼看来,本地函数似乎只是比匿名函数稍好的一种语法。但它实际上还存在几个优点:

  • 首先,你无需为其分配一个委托以保存该函数。这不仅减少了内存压力,同时还允许编译器对该函数进行内联操作。
  • 其次,在创建闭包时,也无需为其分配一个对象,因为它能够直接访问本地变量。这一点同样能够改善性能,因为它也减少了 GC 的压力。

按照第二条规则推算,你将无法创建一个指向本地函数的委托。这一点对于代码的组织其实是一个优点,因为你无需创建独立的函数,并且将现有函数的状态作为显式的参数进行传递。

部分类的改进

最后演示的特性是一种处理部分类的新方式。在过去,部分类的应用是基于代码生成优先的概念而出现的。所生成的代码将包含一系列部分方法,开发者可以选择实现这些方法,以调整类的行为。

通过新的“replace”语法,开发者就多了一种新选择,能够以最直接的方式编写代码,随后再引入代码生成器,并重写这些方法。以下将通过一个简单的示例表现开发者的代码编写方式:

复制代码
public string FirstName {get; set;}

简单又清晰,但完全不符合 XAML 风格应用的写法。因此,代码生成器将生成如下代码:

复制代码
private string m_FirstName;
static readonly PropertyChangedEventArgs s_FirstName_EventArgs =new PropertyChangedEventArgs("FirstName")
replace public string FirstName {
get {
return m_FirstName;
}
set {
if (m_FirstName == value)
return;
m_FirstName = value;
PropertyChanged?.Invoke(this, m_FirstName_EventArg);
}

通过“replace”关键字,所生成的代码将直接替换手写的代码,添加所缺失的功能。在这个示例中,我们甚至还能够处理一些开发者经常会忽略的麻烦的部分,例如对 EventArgs 对象进行缓存。

虽然这个官方示例仅用于属性变更通知,但这一技术还可用于各种“面向切面编程(AOP)”的场景,例如在代码中注入日志记录、安全检查、参数校验以及其他各种繁琐的样板式代码。

如果读者想实际了解一下这些特性,可以观赏 Channel 9 中的视频“ The Future of C#”。

查看英文原文 C# 7 Features Previewed

【微软开发工具】 Visual Studio Community 提供了供个人开发者、开放源代码项目、学术研究、教育和小型专业团队的免费使用,并根据具体需求来创建面向 Windows、Android 和 iOS 的新式 App、WebApp 和云服务。

2016-04-06 19:005635
用户头像

发布了 428 篇内容, 共 191.0 次阅读, 收获喜欢 39 次。

关注

评论

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

GraphX 在图数据库 Nebula Graph 的图计算实践

NebulaGraph

图数据库 图数据库实战

滴滴 Flink-1.10 升级之路

Apache Flink

flink

这只猫在云端定居了?边缘计算在天猫精灵云应用上的落地实践

阿里云Edge Plus

CDN IoT 边缘计算 云桌面

疫情成本遭不住?一招降本85%,架构特性全部公开!

数据君

【内含福利】流行在CDN圈内的黑话有哪些?

阿里云Edge Plus

CDN

Linux-Lab 入门:体验

贾献华

Docker Linux 嵌入式 内核 Lab

MySQL之父,MySQL官方,三大顶会齐赞,凭什么?

数据君

CodeDay#5 全程回顾——一场关于动态化开发实践的技术探讨

蚂蚁集团移动开发平台 mPaaS

mPaaS Codeday 技术沙龙

疫情又反扑,除了不乱跑,我们还能干点啥?

数据君

年终总结:华为|字节|腾讯|京东|网易|滴滴面经分享(斩获6个offer)

Java架构之路

Java 程序员 架构 面试 编程语言

new的过程是怎样的?看完这一篇就懂了

codevald

Java JVM原理 面向对象编程 类对象

2020已过,2021来临,iOS 开发市场如何?一切都是未知!【未来可期】

ios 程序员

List去除重复数据的五种方式

xcbeyond

Java ArrayList 28天写作

技术方案设计的方法论及案例分享

阿里巴巴云原生

数据库 流计算 云原生 监控 存储

字节面试太刁钻了:不加机器,怎么提升系统并发100倍

Java架构师迁哥

云话题 | 第3期 你女朋友在买买买时,程序员小哥在干嘛?

阿里云Edge Plus

CDN 直播 直播带货

教你10分钟解决短信验证码接口被盗刷、轰炸、恶意点击等问题。

香芋味的猫丶

短信防刷 短信验证码 短信防轰炸 短信防火墙

Elasticsearch 从 0 到千万级数据查询实践

📿

Java spring elasticsearch Spring Cloud spring data

LeetCode题解:69. x 的平方根,二分查找,JavaScript,详细注释

Lee Chen

算法 大前端 LeetCode

点赞系统软件开发

luluhulian

云讲堂 | 5期视频带你全面了解滴滴Logi-KafkaManager

滴滴云

kafka 运维 监控 滴滴Logi

2020-21《全球质量报告》解读

BY林子

质量保障 质量赋能 敏捷测试

架构师 3 期 3 班 -week10- 作业

zbest

作业 week10

万字长文详细总结!关于继承、重写与重载、封装、接口的硬核干货

codevald

Java 接口 封装、继承、多态 类对象

Varchar竟然会自动存储成lob类型?

dbaplus社群

让机器人响应更快!阿里云 ARMS 助力深绘智能系统响应时长缩短50%

阿里巴巴中间件

爱奇艺率先上线CUVA HDR标准内容,将多端支持该标准2021央视春晚直播、点播

爱奇艺技术产品团队

Docker开启Remote API 访问 2375端口

wjchenge

Docker 2375端口

十里选一终拿offer,准阿里java程序员分享面试经验!

Java架构之路

Java 程序员 架构 面试 编程语言

如何避免让微服务测试成为研发团队最大的瓶颈?

阿里巴巴中间件

满满的干货!阿里开源Java程序员2021年金三银四面试指南

Java架构之路

Java 程序员 架构 面试 编程语言

C# 7特性预览_.NET_Jonathan Allen_InfoQ精选文章