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   else8   {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 Student2   {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 expressions9           {
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 Student2   {3       private string _name;4   5       // Expression-bodied constructor6       public Student(string name) => _name = name;7   8       // Expression-bodied finalizer9       ~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 Student2   {3       private string _name = GetName() ?? throw new ArgumentNullException(nameof(GetName));4   5       private int _age;6   7       public int Age8       {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)开头,字母不区分大小写;数字分隔符只有三个地方不能写:开头,结尾,小数点前后。

总结:二进制文本,数字分隔符 可使常量值更具可读性

syntax sugar 2相关推荐

  1. 语法糖(Syntax Sugar)Python

    语法糖让程序更加简洁,有更高的可读性 连续比较 1 < x < 10 (x>1 and x<10) 三元表达式(结果一 if  判断条件 else 结果二) 推导表达式 推导表 ...

  2. 有没有办法检查`null`和`undefined`?

    本文翻译自:Is there a way to check for both `null` and `undefined`? Since TypeScript is strongly-typed, s ...

  3. 是否可以将Java 8用于Android开发?

    本文翻译自:Is it possible to use Java 8 for Android development? Searching the web, it is not clear if Ja ...

  4. 什么是C ++ 11中的lambda表达式?

    本文翻译自:What is a lambda expression in C++11? What is a lambda expression in C++11? 什么是C ++ 11中的lambda ...

  5. 37个我爱Ruby的理由

    原著:    http://hypermetrics.com/ruby.html 翻译:liubin http://www.ruby-cn.org/ 一切权利归原作者所有,转载请保留. 2004/12 ...

  6. java foreach用法_Java十大简单性能优化

    以下是Java中最容易进行的10个性能优化: 1.使用StringBuilder 这几乎是所有Java代码中的默认设置.尽量避免+操作员.当然,您可能会争辩说它StringBuilder无论如何都是语 ...

  7. python高阶函数 动态_Python进阶内容(一)--- 高阶函数 High order function

    0. 问题 #本文将围绕这段代码进行Python中高阶函数相关内容的讲解#文中所有代码的兼容性要求为:Python 3.6,IPython 6.1.0 defaddspam(fn):def new(* ...

  8. 【Java6】Date类/Calendar类,System类/Math类,包装类,集合,泛型,内部类

    文章目录 1.Date类:getTime(),SimpleDateFormat 2.Calendar类:只有子类对象才能向上转型 3.System类:System.exit(0) 4.Math类:ce ...

  9. Java程序员应知道的十条Java优化策略,让你的系统健步如飞

    1.使用StringBuilder(技术文) StingBuilder 应该是在我们的Java代码中默认使用的,应该避免使用 + 操作符.或许你会对 StringBuilder 的语法糖(syntax ...

  10. java 性能 优化_Java十大简单性能优化

    java 性能 优化 关于" web scale "这个流行词有很多炒作,人们花了很多时间来重新组织他们的应用程序体系结构,以使其系统"规模化". 但是什么是扩 ...

最新文章

  1. objective-C NSNotificationCenter (通知)的使用方法
  2. SAP BSP应用configuration的加载原理
  3. 用html写出生日蛋糕,纯HTML5+CSS3制作生日蛋糕代码
  4. 母版页嵌套子母版页通信
  5. oracle liunx 6.5安装目录,linux redhat 6.5 多路径配置
  6. c语言中0xof423什么意思,C语言指针 百思不得其解的一个问题
  7. php sql慢查询,Select多行SQL语句慢查询优化
  8. BZOJ 1013: [JSOI2008]球形空间产生器sphere
  9. 推荐一些学习SEO的优秀书籍附pdf电子书下载地址
  10. 南山驿站机器人_fc机器人大战钢铁之魂攻略
  11. Android进阶宝典—App响应时间优化
  12. join left 大数据_left join 大数据
  13. 离散数学 2. 范式、推理
  14. 抬杠计算机,全 自 动 抬 杠 机
  15. burp如何设置微信小程序代理
  16. python opencv 为图片添加alpha通道并设置透明,判断是否存在alpha通道
  17. 小熊派简介和环境搭建
  18. 网站渗透测试服务 squid反向代理代码执行漏洞的挖掘
  19. 一、 镜头主要参数工业相机镜头的参数与选型
  20. TM1637调试记录

热门文章

  1. Maya 界面与视图2
  2. 一级域名怎么申请二级域名?
  3. lbp2900linux驱动下载,佳能lbp2900打印机驱动下载|佳能lbp2900打印机驱动下载 通用版_小皮网...
  4. 计算机上面的用户帐户怎么删除,怎么删除用户帐户的教程详解【图解】
  5. 自制XILINX FPGA JTAG下载器
  6. 关于CSDN原力计划活动说明
  7. [深度学习]卷积神经网络CNN - 一张图理解滤波器/层数/深度
  8. django接入facebook登陆设置
  9. 电脑录屏怎么录?3个方法,教你如何录制视频
  10. Ruby / Rails代码气味基础01