[C#7] 1.Tuples(元组)
1. 老版本代码
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 var fullName = GetFullName();
6
7 Console.WriteLine(fullName.Item1);// Item1,2,3不能忍,,,
8 Console.WriteLine(fullName.Item2);
9 Console.WriteLine(fullName.Item3);
10 }
11 static Tuple<string, string, string> GetFullName() => new Tuple<string, string, string>("first name", "blackheart", "last name");
12 }
在有些场景下,我们需要一个方法返回一个以上的返回值,微软在.NET 4中引入了Tuple这个泛型类,可以允许我们返回多个参数,每个参数按照顺序被命名为 Item1;Item2,Item3 ,算是部分的解决了我们的问题,但是对于强迫症程序员来说,Item1,2,3的命名简直是不能忍的,,,so,在C#7中,引入了一个新的泛型类型ValueTuple<T>来解决这个问题,这个类型位于一个单独的dll(System.ValueTuple)中,可以通过nuget来引入到你当前的项目中(https://www.nuget.org/packages/System.ValueTuple/)。
2. ValueTuple
不废话,直接看代码:
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 var fullName = GetFullName();
6
7 Console.WriteLine(fullName.First); // 终于可以不是Item1,2,3了,,,
8 Console.WriteLine(fullName.Middle);
9 Console.WriteLine(fullName.Last);
10 }
11
12 static (string First, string Middle, string Last) GetFullName() => ("first name", "blackheart", "last name");
13 }
看出来差别了吗?我们终于可以用更直观的名字来替换掉该死的"Item1,2,3"了,看起来很棒吧。但是貌似我们并没有用到上面我提到的System.ValueTuple,我们翻开编译后的程序集看看:
1 internal class Program
2 {
3 private static void Main(string[] args)
4 {
5 ValueTuple<string, string, string> fullName = Program.GetFullName();
6 Console.WriteLine(fullName.Item1); // 原来你还是Item1,2,3,,,FUCK!!!
7 Console.WriteLine(fullName.Item2);
8 Console.WriteLine(fullName.Item3);
9 }
10
11 [TupleElementNames(new string[]
12 {
13 "First",
14 "Middle",
15 "Last"
16 })]
17 private static ValueTuple<string, string, string> GetFullName()
18 {
19 return new ValueTuple<string, string, string>("first name", "blackheart", "last name");
20 }
21 }
不看不知道,一看吓一跳,原来我们的 fullName.First; 编译后居然还是 fullName.Item1 ,真是日了狗了。。。
不同之处在于GetFullName这个方法,编译器把我们简化的语法形式翻译成了 ValueTuple<string, string, string> ,还给加了一个新的Attribute(TupleElementNamesAttribute),然后把我们自定义的非常直观友好的“First”,"Middle","Last"当作元数据给存起来了(如果只是局部使用,则不会添加这样的元数据)。TupleElementNamesAttribute和ValueTuple一样,位于System.ValueTuple的单独dll中。
3. Example
1 class Program
2 {
3 static void Main(string[] args)
4 {
5 var range = (first: 1, end: 10);
6 //也可以这样写,效果是一样的,编译后都是没有了first,end的痕迹,,,first和end只是语法层面的障眼法
7 //(int first, int last) range = (1, 10);
8 Console.WriteLine(range.first);
9 Console.WriteLine(range.end);
10
11 //可以使用var,这种无显示声明一个变量的方式会编译出多余的代码,慎用,不知是不是还未优化好。
12 (var begin, var end) = (DateTime.Parse("2017-1-1"), DateTime.Parse("2017-12-31"));
13 Console.WriteLine(begin);
14 Console.WriteLine(end);
15
16 //begin,end可以被覆盖重命名为startDate和endDate,但是会有一个编译警告,提示名字被忽略掉了。
17 //warning CS8123: The tuple element name 'begin' is ignored because a different name is specified by the target type '(DateTime startDate, DateTime endDate)'
18 //warning CS8123: The tuple element name 'end' is ignored because a different name is specified by the target type '(DateTime startDate, DateTime endDate)‘
19 (DateTime startDate, DateTime endDate) timeSpan = (begin: DateTime.Parse("2017-1-1"), end: DateTime.Parse("2017-12-31"));
20 Console.WriteLine(timeSpan.startDate);
21 Console.WriteLine(timeSpan.endDate);
22 }
23 }
look一下编译后的代码:
1 private static void Main(string[] args)
2 {
3 ValueTuple<int, int> range = new ValueTuple<int, int>(1, 10);
4 Console.WriteLine(range.Item1);
5 Console.WriteLine(range.Item2);
6 ValueTuple<DateTime, DateTime> expr_3C = new ValueTuple<DateTime, DateTime>(DateTime.Parse("2017-1-1"), DateTime.Parse("2017-12-31"));
7 DateTime item = expr_3C.Item1;
8 DateTime item2 = expr_3C.Item2;
9 DateTime begin = item;
10 DateTime end = item2;
11 Console.WriteLine(begin);
12 Console.WriteLine(end);
13 ValueTuple<DateTime, DateTime> timeSpan = new ValueTuple<DateTime, DateTime>(DateTime.Parse("2017-1-1"), DateTime.Parse("2017-12-31"));
14 Console.WriteLine(timeSpan.Item1);
15 Console.WriteLine(timeSpan.Item2);
16 }
注意 (var begin, var end) = (DateTime.Parse("2017-1-1"), DateTime.Parse("2017-12-31")); 这一行的便宜结果,看起来很是糟糕(上述6-10行红色部分),可能还是编译优化不足的问题吧(release编译也是如此)。
4. 总结
新的语法形式确实直观友好了好多,but,本质依然是借助泛型类型来实现的,同时也需要编译器对新语法形式的支持。
了解了本质是什么东西之后,以后在项目中环境允许的话,就放心大胆的使用吧(类型ValueTuple可以出现的地方,(first,last)这种新语法形式均可以)。
- pytorch自然语言处理之Pooling层的句子分类
- su命令cannot set groups: Operation not permitted的解决方法
- 利用腾讯云 COS 云对象存储定时远程备份网站
- 腾讯云技术公开课:零基础入门高可用云端架构设计
- 包学会之浅入浅出Vue.js:开学篇
- 包学会之浅入浅出Vue.js:升学篇
- 一个只有99行代码的JS流程框架 (一)
- 【腾讯云的1001种玩法】试用腾讯云 Windows Server 2012 R2 镜像的几点经验分享
- 一个只有99行代码的JS流程框架(二)
- 看书的时候如何调试书中简单的C+代码?
- gcForest 集成学习方法的 Python 实现
- 云端架构师养成系列之一:高性能云硬盘入门与实战(视频)
- 云端架构师养成系列之二:云端负载均衡上手与实践
- 微信 PaxosStore:海量数据冷热分级架构
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 设计模式(一):Android 源码中的单例模式
- Flask(数据库操作 十一)
- Golang | 既是接口又是类型,interface是什么神仙用法?
- 剑指offer第11题:机器人运动范围
- SQL 查找是否"存在",别再 count 了,很耗费时间的!
- 【LeeCode 中等 字节 python3】567. 字符串的排列
- Spark实现WordCount的几种方式总结
- Redis6之pub/sub发布与订阅(对比List和Kafka)
- SpringBoot原理?属性配置?在这里
- 【LeeCode 中等 矩阵】面试题 01.07. 旋转矩阵
- pytest文档44-allure.dynamic动态生成用例标题
- Java自动化测试(TestNg参数化 11)
- 宋宝华:论Linux的页迁移(Page Migration)上集
- 火遍全网的自热火锅哪款最好吃?我们用Python告诉你!
- 剑指offer第10题:矩阵中的路径