30分钟掌握 C#7

1. out 变量(out variables)

以前我们使用out变量必须在使用前进行声明,C# 7.0 给我们提供了一种更简洁的语法 “使用时进行内联声明” 。如下所示:

1   var input = ReadLine();
2   if (int.TryParse(input, out var result))
3   {
4       WriteLine("您输入的数字是:{0}", input);
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}", s);
 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才能提供如此优美的语法。

  使用 ValueType 则需要导入: 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)开头,字母不区分大小写;数字分隔符只有三个地方不能写:开头,结尾,小数点前后。

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

 下一篇 C# 的异步讲解....

时间: 2024-10-18 17:08:21

30分钟掌握 C#7的相关文章

30分钟掌握ES6核心内容

ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准.因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015. 也就是说,ES6就是ES2015. 虽然目前并不是所有浏览器都能兼容ES6全部特性,但越来越多的程序员在实际项目当中已经开始使用ES6了.所以就算你现在不打算使用ES6,但为了看懂别人的你也该懂点ES6的语法了... 在我们正式讲解ES6语法之前,我们得先了解下Babel. Babel Babel是一个广泛使用的ES6转码器,可以将

【C++11】30分钟了解C++11新特性

作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 什么是C++11 C++11是曾经被叫做C++0x,是对目前C++语言的扩展和修正,C++11不仅包含核心语言的新机能,而且扩展了C++的标准程序库(STL),并入了大部分的C++ Technical Report 1(TR1)程序库(数学的特殊函数除外). C++11包括大量的新特性:包括lambda表达式,类型推导关键字auto.decl

DAX基础入门 – 30分钟从SQL到DAX — PowerBI 利器

看到漂漂亮亮的PowerBI报表,手痒痒怎么办?! 有没有面对着稀奇古怪的DAX而感到有点丈八金刚摸不着头脑或者干瞪眼?! 有没有想得到某个值想不出来DAX怎么写而直跳脚!? 看完这篇文章,你会恍然大悟,捂脸偷笑.呼呼呼~ 前言: 这篇文章对于具有一点SQL查询基础人会十分容易理解,譬如:掌握SELECT,SUM,GROUP BY等. 注:此文不涉及到Filter Context(筛选上下文)的介绍. 正文: 对于对SQL有一定了解的人来说,咋看DAX,怎么都不习惯. 但是,如果理解以下几个后,

es6属性基础教学,30分钟包会

ES6基础智商划重点在实际开发中,ES6已经非常普及了.掌握ES6的知识变成了一种必须.尽管我们在使用时仍然需要经过babel编译. ES6彻底改变了前端的编码风格,可以说对于前端的影响非常巨大.值得高兴的是,如果你熟悉ES5,学习ES6并不需要花费太多的时间就可以掌握,因为常用的基础语法并不多,花少量的时间,就可以开始我们的ES6之旅了. 这篇文章不会详细的告诉你ES6的每一个细节知识,只会根据我自己的开发经验,将我在实际开发中常常用到的知识点分享给大家,给大家学习ES6一个方向的指引.这是因

正则表达式30分钟入门教程

目录 跳过目录 本文目标 如何使用本教程 正则表达式到底是什么东西? 入门 测试正则表达式 元字符 字符转义 重复 字符类 分枝条件 反义 分组 后向引用 零宽断言 负向零宽断言 注释 贪婪与懒惰 处理选项 平衡组/递归匹配 还有些什么东西没提到 联系作者 网上的资源及本文参考文献 更新纪录 本文目标 30分钟内让你明白正则表达式是什么,并对它有一些基本的了解,让你可以在自己的程序或网页里使用它. 如何使用本教程 最重要的是--请给我30分钟,如果你没有使用正则表达式的经验,请不要试图在30秒内

30分钟快速搭建Web CRUD的管理平台--django神奇魔法

加上你的准备的时间,估计30分钟完全够用了,因为最近在做爬虫管理平台,想着快速开发,没想到python web平台下有这么非常方便的框架,简洁而优雅.将自己的一些坑总结出来,方便给大家的使用. 准备环境: 系统:win7 or ubuntu django版本:1.8.5 python版本:2.7.6 数据库:自带的SQLLITE3 IDE: sublime text 3 ===========================Read ? go===========================

【转】正则表达式30分钟入门教程

首页 | 常用正则表达式 | 正则表达式测试工具 正则表达式30分钟入门教程 版本:v2.33 (2013-1-10) 作者:deerchao 转载请注明来源 目录 跳过目录 本文目标 如何使用本教程 正则表达式到底是什么东西? 入门 测试正则表达式 元字符 字符转义 重复 字符类 分枝条件 反义 分组 后向引用 零宽断言 负向零宽断言 注释 贪婪与懒惰 处理选项 平衡组/递归匹配 还有些什么东西没提到 联系作者 网上的资源及本文参考文献 更新纪录 本文目标 30分钟内让你明白正则表达式是什么,

30分钟学会正则表达式

正则表达式30分钟入门教程 版本:v2.33 (2013-1-10) 作者:deerchao 转载请注明来源 原地址:http://www.jb51.net/tools/zhengze.html#mission 目录 跳过目录 本文目标 如何使用本教程 正则表达式到底是什么东西? 入门 测试正则表达式 元字符 字符转义 重复 字符类 分枝条件 反义 分组 后向引用 零宽断言 负向零宽断言 注释 贪婪与懒惰 处理选项 平衡组/递归匹配 还有些什么东西没提到 联系作者 网上的资源及本文参考文献 更新

HTML 30分钟入门教程

本文目标 30分钟内让你明白HTML是什么,并对它有一些基本的了解.一旦入门后,你可以从网上找到更多更详细的资料来继续学习. 什么是HTML HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,它规定了自己的语法规则,用来表示比"文本"更丰富的意义,比如图片,表格,链接等.浏览器(IE,FireFox等)软件知道HTML语言的语法,可以用来查看HTML文档.目前互联网上的绝大部分网页都是使用HTML编写的. HTML是什么样的 简单地来说,HT

30分钟掌握ES6/ES2015核心内容

30分钟掌握ES6/ES2015核心内容 ECMAScript 6(以下简称ES6)是JavaScript语言的下一代标准.因为当前版本的ES6是在2015年发布的,所以又称ECMAScript 2015. 也就是说,ES6就是ES2015. 虽然目前并不是所有浏览器都能兼容ES6全部特性,但越来越多的程序员在实际项目当中已经开始使用ES6了.所以就算你现在不打算使用ES6,但为了看懂别人的你也该懂点ES6的语法了... 在我们正式讲解ES6语法之前,我们得先了解下Babel.Babel Bab