详解C#7.0新特性
1. out 变量(out variables)
以前我们使用out变量必须在使用前进行声明,C# 7.0 给我们提供了一种更简洁的语法 “使用时进行内联声明” 。如下所示:
1 var input = ReadLine();
2 if (int.TryParse(input, out var result))
3 {
4 WriteLine("您输入的数字是:{0}",result);
5 }
6 else
7 {
8 WriteLine("无法解析输入...");
9 }
上面代码编译后:
1 int num;
2 string s = Console.ReadLine();
3 if (int.TryParse(s, out num))
4 {
5 Console.WriteLine("您输入的数字是:{0}", num);
6 }
7 else
8 {
9 Console.WriteLine("无法解析输入...");
10 }
原理解析:所谓的 “内联声明” 编译后就是以前的原始写法,只是现在由编译器来完成。
备注:在进行内联声明时,即可直接写明变量的类型也可以写隐式类型,因为out关键字修饰的一定是局部变量。
2. 元组(Tuples)
元组(Tuple)在 .Net 4.0 的时候就有了,但元组也有些缺点,如:
1)Tuple 会影响代码的可读性,因为它的属性名都是:Item1,Item2.. 。
2)Tuple 还不够轻量级,因为它是引用类型(Class)。
备注:上述所指 Tuple 还不够轻量级,是从某种意义上来说的或者是一种假设,即假设分配操作非常的多。
C# 7 中的元组(ValueTuple)解决了上述两个缺点:
1)ValueTuple 支持语义上的字段命名。
2)ValueTuple 是值类型(Struct)。
1. 如何创建一个元组?
1 var tuple = (1, 2); // 使用语法糖创建元组
2 var tuple2 = ValueTuple.Create(1, 2); // 使用静态方法【Create】创建元组
3 var tuple3 = new ValueTuple<int, int>(1, 2); // 使用 new 运算符创建元组
4
5 WriteLine($"first:{tuple.Item1}, second:{tuple.Item2}, 上面三种方式都是等价的。");
原理解析:上面三种方式最终都是使用 new 运算符来创建实例。
2. 如何创建给字段命名的元组?
1 // 左边指定字段名称
2 (int one, int two) tuple = (1, 2);
3 WriteLine($"first:{tuple.one}, second:{tuple.two}");
4
5 // 右边指定字段名称
6 var tuple2 = (one: 1, two: 2);
7 WriteLine($"first:{tuple2.one}, second:{tuple2.two}");
8
9 // 左右两边同时指定字段名称
10 (int one, int two) tuple3 = (first: 1, second: 2); /* 此处会有警告:由于目标类型(xx)已指定了其它名称,因为忽略元组名称xxx */
11 WriteLine($"first:{tuple3.one}, second:{tuple3.two}");
注:左右两边同时指定字段名称,会使用左边的字段名称覆盖右边的字段名称(一一对应)。
原理解析:上述给字段命名的元组在编译后其字段名称还是:Item1, Item2...,即:“命名”只是语义上的命名。
3. 什么是解构?
解构顾名思义就是将整体分解成部分。
4. 解构元组,如下所示:
1 var (one, two) = GetTuple();
2
3 WriteLine($"first:{one}, second:{two}");
1 static (int, int) GetTuple()
2 {
3 return (1, 2);
4 }
原理解析:解构元组就是将元组中的字段值赋值给声明的局部变量(编译后可查看)。
备注:在解构时“=”左边能提取变量的数据类型(如上所示),元组中字段类型相同时即可提取具体类型也可以是隐式类型,但元组中字段类型
不相同时只能提取隐式类型。
5. 解构可以应用于 .Net 的任意类型,但需要编写 Deconstruct 方法成员(实例或扩展)。如下所示:
1 public class Student
2 {
3 public Student(string name, int age)
4 {
5 Name = name;
6 Age = age;
7 }
8
9 public string Name { get; set; }
10
11 public int Age { get; set; }
12
13 public void Deconstruct(out string name, out int age)
14 {
15 name = Name;
16 age = Age;
17 }
18 }
使用方式如下:
1 var (Name, Age) = new Student("Mike", 30);
2
3 WriteLine($"name:{Name}, age:{Age}");
原理解析:编译后就是由其实例调用 Deconstruct 方法,然后给局部变量赋值。
Deconstruct 方法签名:
1 // 实例签名
2 public void Deconstruct(out type variable1, out type variable2...)
3
4 // 扩展签名
5 public static void Deconstruct(this type instance, out type variable1, out type variable2...)
总结:1. 元组的原理是利用了成员类型的嵌套或者是说成员类型的递归。2. 编译器很牛B才能提供如此优美的语法。
使用 ValueTuple 则需要导入: Install - Package System.ValueTuple
3. 模式匹配(Pattern matching)
1. is 表达式(is expressions),如:
1 static int GetSum(IEnumerable<object> values)
2 {
3 var sum = 0;
4 if (values == null) return sum;
5
6 foreach (var item in values)
7 {
8 if (item is short) // C# 7 之前的 is expressions
9 {
10 sum += (short)item;
11 }
12 else if (item is int val) // C# 7 的 is expressions
13 {
14 sum += val;
15 }
16 else if (item is string str && int.TryParse(str, out var result)) // is expressions 和 out variables 结合使用
17 {
18 sum += result;
19 }
20 else if (item is IEnumerable<object> subList)
21 {
22 sum += GetSum(subList);
23 }
24 }
25
26 return sum;
27 }
使用方法:
1 条件控制语句(obj is type variable)
2 {
3 // Processing...
4 }
原理解析:此 is 非彼 is ,这个扩展的 is 其实是 as 和 if 的组合。即它先进行 as 转换再进行 if 判断,判断其结果是否为 null,不等于 null 则执行
语句块逻辑,反之不行。由上可知其实C# 7之前我们也可实现类似的功能,只是写法上比较繁琐。
2. switch语句更新(switch statement updates),如:
1 static int GetSum(IEnumerable<object> values)
2 {
3 var sum = 0;
4 if (values == null) return 0;
5
6 foreach (var item in values)
7 {
8 switch (item)
9 {
10 case 0: // 常量模式匹配
11 break;
12 case short sval: // 类型模式匹配
13 sum += sval;
14 break;
15 case int ival:
16 sum += ival;
17 break;
18 case string str when int.TryParse(str, out var result): // 类型模式匹配 + 条件表达式
19 sum += result;
20 break;
21 case IEnumerable<object> subList when subList.Any():
22 sum += GetSum(subList);
23 break;
24 default:
25 throw new InvalidOperationException("未知的类型");
26 }
27 }
28
29 return sum;
30 }
使用方法:
1 switch (item)
2 {
3 case type variable1:
4 // processing...
5 break;
6 case type variable2 when predicate:
7 // processing...
8 break;
9 default:
10 // processing...
11 break;
12 }
原理解析:此 switch 非彼 switch,编译后你会发现扩展的 switch 就是 as 、if 、goto 语句的组合体。同 is expressions 一样,以前我们也能实
现只是写法比较繁琐并且可读性不强。
总结:模式匹配语法是想让我们在简单的情况下实现类似与多态一样的动态调用,即在运行时确定成员类型和调用具体的实现。
4. 局部引用和引用返回 (Ref locals and returns)
我们知道 C# 的 ref 和 out 关键字是对值传递的一个补充,是为了防止值类型大对象在Copy过程中损失更多的性能。现在在C# 7中 ref 关键字得
到了加强,它不仅可以获取值类型的引用而且还可以获取某个变量(引用类型)的局部引用。如:
1 static ref int GetLocalRef(int[,] arr, Func<int, bool> func)
2 {
3 for (int i = 0; i < arr.GetLength(0); i++)
4 {
5 for (int j = 0; j < arr.GetLength(1); j++)
6 {
7 if (func(arr[i, j]))
8 {
9 return ref arr[i, j];
10 }
11 }
12 }
13
14 throw new InvalidOperationException("Not found");
15 }
Call:
1 int[,] arr = { { 10, 15 }, { 20, 25 } };
2 ref var num = ref GetLocalRef(arr, c => c == 20);
3 num = 600;
4
5 Console.WriteLine(arr[1, 0]);
Print results:
使用方法:
1. 方法的返回值必须是引用返回:
a) 声明方法签名时必须在返回类型前加上 ref 修饰。
b) 在每个 return 关键字后也要加上 ref 修饰,以表明是返回引用。
2. 分配引用(即赋值),必须在声明局部变量前加上 ref 修饰,以及在方法返回引用前加上 ref 修饰。
注:C# 开发的是托管代码,所以一般不希望程序员去操作指针。并由上述可知在使用过程中需要大量的使用 ref 来标明这是引用变量(编译后其
实没那么多),当然这也是为了提高代码的可读性。
总结:虽然 C# 7 中提供了局部引用和引用返回,但为了防止滥用所以也有诸多约束,如:
1. 你不能将一个值分配给 ref 变量,如:
1 ref int num = 10; // error:无法使用值初始化按引用变量
2. 你不能返回一个生存期不超过方法作用域的变量引用,如:
1 public ref int GetLocalRef(int num) => ref num; // error: 无法按引用返回参数,因为它不是 ref 或 out 参数
3. ref 不能修饰 “属性” 和 “索引器”。
1 var list = new List<int>();
2 ref var n = ref list.Count; // error: 属性或索引器不能作为 out 或 ref 参数传递
原理解析:非常简单就是指针传递,并且个人觉得此语法的使用场景非常有限,都是用来处理大对象的,目的是减少GC提高性能。
5. 局部函数(Local functions)
C# 7 中的一个功能“局部函数”,如下所示:
1 static IEnumerable<char> GetCharList(string str)
2 {
3 if (IsNullOrWhiteSpace(str))
4 throw new ArgumentNullException(nameof(str));
5
6 return GetList();
7
8 IEnumerable<char> GetList()
9 {
10 for (int i = 0; i < str.Length; i++)
11 {
12 yield return str[i];
13 }
14 }
15 }
使用方法:
1 [数据类型,void] 方法名([参数])
2 {
3 // Method body;[] 里面都是可选项
4 }
原理解析:局部函数虽然是在其他函数内部声明,但它编译后就是一个被 internal 修饰的静态函数,它是属于类,至于它为什么能够使用上级函
数中的局部变量和参数呢?那是因为编译器会根据其使用的成员生成一个新类型(Class/Struct)然后将其传入函数中。由上可知则局部函数的声
明跟位置无关,并可无限嵌套。
总结:个人觉得局部函数是对 C# 异常机制在语义上的一次补充(如上例),以及为代码提供清晰的结构而设置的语法。但局部函数也有其缺点,
就是局部函数中的代码无法复用(反射除外)。
6. 更多的表达式体成员(More expression-bodied members)
C# 6 的时候就支持表达式体成员,但当时只支持“函数成员”和“只读属性”,这一特性在C# 7中得到了扩展,它能支持更多的成员:构造函数
、析构函数、带 get,set 访问器的属性、以及索引器。如下所示:
1 public class Student
2 {
3 private string _name;
4
5 // Expression-bodied constructor
6 public Student(string name) => _name = name;
7
8 // Expression-bodied finalizer
9 ~Student() => Console.WriteLine("Finalized!");
10
11 // Expression-bodied get / set accessors.
12 public string Name
13 {
14 get => _name;
15 set => _name = value ?? "Mike";
16 }
17
18 // Expression-bodied indexers
19 public string this[string name] => Convert.ToBase64String(Encoding.UTF8.GetBytes(name));
20 }
备注:索引器其实在C# 6中就得到了支持,但其它三种在C# 6中未得到支持。
7. Throw 表达式(Throw expressions)
异常机制是C#的重要组成部分,但在以前并不是所有语句都可以抛出异常的,如:条件表达式(? :)、null合并运算符(??)、一些Lambda
表达式。而使用 C# 7 您可在任意地方抛出异常。如:
1 public class Student
2 {
3 private string _name = GetName() ?? throw new ArgumentNullException(nameof(GetName));
4
5 private int _age;
6
7 public int Age
8 {
9 get => _age;
10 set => _age = value <= 0 || value >= 130 ? throw new ArgumentException("参数不合法") : value;
11 }
12
13 static string GetName() => null;
14 }
8. 扩展异步返回类型(Generalized async return types)
以前异步的返回类型必须是:Task、Task<T>、void,现在 C# 7 中新增了一种类型:ValueTask<T>,如下所示:
1 public async ValueTask<int> Func()
2 {
3 await Task.Delay(3000);
4 return 100;
5 }
总结:ValueTask<T> 与 ValueTuple 非常相似,所以就不列举: ValueTask<T> 与 Task 之间的异同了,但它们都是为了优化特定场景性能而
新增的类型。
使用 ValueTask<T> 则需要导入: Install - Package System.Threading.Tasks.Extensions
9. 数字文本语法的改进(Numeric literal syntax improvements)
C# 7 还包含两个新特性:二进制文字、数字分隔符,如下所示:
1 var one = 0b0001;
2 var sixteen = 0b0001_0000;
3
4 long salary = 1000_000_000;
5 decimal pi = 3.141_592_653_589m;
注:二进制文本是以0b(零b)开头,字母不区分大小写;数字分隔符只有三个地方不能写:开头,结尾,小数点前后。
总结:二进制文本,数字分隔符 可使常量值更具可读性。
作者:雨夜潇湘 QQ群:
原文:30分钟掌握 C#7
出处:http://www.cnblogs.com/VVStudy/p/6551300.html
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
详解C#7.0新特性的更多相关文章
- Java lambda 表达式详解(JDK 8 新特性)
什么是 lambda 表达式 lambda 表达式(拉姆达表达式)是 JAVA 8 中提供的一种新的特性,它使 Java 也能进行简单的"函数式编程". lambda 表达式的本质 ...
- iOS 9界面适配利器:详解Xcode 7的新特性UIStackView
升级Xcode7后老项目storyBoard出现问题了,一看时新特性搞的鬼.具体 详见:http://www.csdn.net/article/2015-08-04/2825372
- [转]Servlet 3.0 新特性详解
原文地址:http://blog.csdn.net/xiazdong/article/details/7208316 Servlet 3.0 新特性概览 1.Servlet.Filter.Listen ...
- Servlet 3.0 新特性详解
转自:http://www.ibm.com/developerworks/cn/java/j-lo-servlet30/#major3 Servlet 是 Java EE 规范体系的重要组成部分,也是 ...
- 【转帖】Servlet 3.0 新特性详解
http://www.ibm.com/developerworks/cn/java/j-lo-servlet30/ Servlet 3.0 新特性概述 Servlet 3.0 作为 Java EE 6 ...
- Servlet 3.0 新特性详解 (转载)
原文地址:https://www.ibm.com/developerworks/cn/java/j-lo-servlet30/ Servlet 3.0 新特性概述 Servlet 3.0 作为 Jav ...
- HTTP详解(3)-http1.0 和http1.1 区别
HTTP详解(3)-http1.0 和http1.1 区别 分类: 网络知识2013-03-17 16:51 1685人阅读 评论(0) 收藏 举报 目录(?)[+] 翻了下HTTP1.1的协 ...
- C#7.0&6.0新特性 — 完整版
C#2.0 泛型 部分类型 匿名方法 迭代器 可空类型 Getter / setter单独可访问性 方法组转换(代表) Co- and Contra-variance for delegates 静态 ...
- webpack 4.0.0-beta.0 新特性介绍
webpack 可以看做是模块打包机.它做的事情是:分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式 ...
随机推荐
- DevOps之基础设施-电力
唠叨话 关于噢屁事的知识点,仅提供精华汇总,具体知识点细节,参考教程网址,如需帮助,请留言. <基础设施-电力> 关于基础设施的电力部分,知识与技能的层次(知道.理解.运用),理论与实践的 ...
- http请求工具-OkHttp用法
OKHttp介绍 okhttp是一个第三方类库,用于android中请求网络.这是一个开源项目,是安卓端最火热的轻量级框架,由移动支付Square公司贡献(该公司还贡献了Picasso和LeakCan ...
- .NET笔试题集(三)
转载于:http://www.cnblogs.com/ForEvErNoME/archive/2012/09/09/2677415.html 1.传入某个属性的set方法的隐含参数的名称是什么? va ...
- systemd 之 journalctl
Systemd 日志系统 一.前言 昨天写了一篇文章,内容为:Systemd 常规操作与彩蛋,参考了 ArchLinux 官方文档并结合培训中的思路进行了部分修改补充.如果你懂得了基础的管理,那必然还 ...
- MySQL-死锁查询
1.查询是否锁表 show OPEN TABLES where In_use > 0; 查询到相对应的进程 === 然后 kill id 2.查询进程 show processlist 补 ...
- python字典的基本操作
字典的基本方法 什么是字典: 字典是一种 key - value的数据类型,听alex说就像我们上学用的字典,通过笔划,字母来查找对饮页面的详细内容. 语法: id_dict = { 'stu1101 ...
- dll动态链接库导出函数方法 -- 静态导出(__declspec前缀导出)
简介 在之前已经笔者已经写过利用.def文件进行dll函数动态导出的文章,那么今天就给大家介绍一下,如何利用**__declspec**函数前缀进行简单的静态函数导出. 要点 大家阅读过动态导出的文章 ...
- Qt在控件未显示时如何获取正确的控件尺寸
因为打算全屏显示一个对话框,而对话框内有几个QLabel的尺寸要在确定QLabel可用的最大尺寸后,再根据内容调整一次,所以在对话框构造函数内就想确定QLabel的最大尺寸,但因为QWidget::u ...
- jQuery插件实例七:一棵Tree的生成史
在需要表示级联.层级的关系中,Tree作为最直观的表达方式常出现在组织架构.权限选择等层级关系中.典型的表现形试类似于: 一颗树的生成常常包括三个部分:1)数据库设计:2)后台程序:3)前端代码.那么 ...
- MySQL权限管理分配
之前一直忽视了MySQL的权限这一块的内容,以为一般般的知识点,随时用随时学就好了,导致自己这方面稍微有点不太明白的地方,总是踩坑,所以后来就总结一下: 1.MySQL权限系统的工作原理 .对连接的用 ...