第一部分:
C#是一种通用的, 类型安全的, 面向对象的编程语言. 有如下特点:
(1)面向对象: c# 是面向对象的范例的一个丰富实现, 它包括封装, 继承和多态性. C# 面向对象的行为包括:
统一的类型系统
类与接口
属性, 方法, 事件
(2)类型安全: C#还允许通过 dynamic 关键字动态指定类型. 但是, C#仍然是一个主要的静态类型语言. 之所以是一种强类型的语言, 是因为它的类型规则是非常严格的, 例如, 不能够使用一个 float 类型的参数去调用一个解释 int 类型的函数, 除非显式的把 float 转换为 int , 这样有助于防止编码错误.
(3)内存管理: C# 依赖运行时的自动内存管理, 它的公共语言运行库有一个垃圾回收器, 在合适非时间回收不再引用的对象所占的空间, 这就释放了程序员手动释放对象的内存. C# 并没有消除指针, 它只是使大多数编程任务不需要使用指针, 对于性能要求高的地方, 还是可以使用指针的, 但是只允许在显式标记为不安全的代码块中使用.
(4)C# 和 CLR:C# 是依赖 runtime 提供的内存管理和异常处理, CLR 允许开发者使用不同的语言建立应用程序. C#是被编译成托管代码的几种托管语言之一. 托管代码以中间语言或 IL 表示. CLR 把 IL 转换成机器的本地代码, 例如 X86 或 X64, 通常就在此之前执行, 这被称为即时 (Just-In-Time,JIT) 编译. 提前时间编译也可用于改善大型程序集的启动时间, 或资源受限的设备. 元数据的存在允许, 程序集引用其他程序集中的类型而不需要额外的文件.
(5)CRL 和 .Net Framework 的关系
.NET Framework 由 CLR 和大量的库组成; 该类库中包含核心类库 (也就是基础类库 BCL) 和应用类库, 应用类库又依赖核心类库
婆婆妈妈说了这么多, 我相信大家都知道, 好了, 下面我们通过代码来看看 C#7.0 到底有哪些让你拍手叫好的地方.
第二部分: C#7.0 新增的功能
(1)数字字面量的提升:
C#7 中的数字文字可以包含下划线以提高可读性, 这些被称为数字分隔符, 并被编译器忽略.
代码如下:
运行结果:
注意: 二进制文字可以用 0b 前缀指定.
所以见到这种写法你不要惊讶, 只是为了提高可读性.
(2)Out variables and discards(接收 out 变量和丢弃 out 变量)
代码:
以前我们的写法:
现在 C#7.0 中可以这样写:
我们不需要在外面先定义好要接收值的变量, 而是直接在里面写, 是不是代码更简洁, 另外一个有趣的地方是, 当一个方法要返回多个值的时候, 我们可以使用 out _, 来选择性的接收返回来的值, 在上面图中的代码中, 方法 SomeBigMethod 返回四个值, 但是我们在接收它返回来的值时, 可以使用 out _不接收返回来的值, 而使用 out int x, 来接收返回来的值, 是不是很灵活.
代码运行结果如下:
ILSpy 结果:
- // Methods
- .method private hidebysig static
- void Main (
- string[] args
- ) cil managed
- {
- // Method begins at RVA 0x2050
- // Code size 49 (0x31)
- .maxstack 4
- .entrypoint
- .locals init (
- [0] int32,
- [1] bool,
- [2] int32,
- [3] int32,
- [4] int32,
- [5] int32
- )
- // (no C# code)
- IL_0000: nop
- // bool successful = int.TryParse("123", out result);
- IL_0001: ldstr "123"
- IL_0006: ldloca.s 0
- IL_0008: call bool [System.Runtime]System.Int32::TryParse(string, int32&)
- IL_000d: stloc.1
- // SomeBigMethod(out int _, out int _, out int x, out int _);
- IL_000e: ldloca.s 3
- IL_0010: ldloca.s 4
- IL_0012: ldloca.s 2
- IL_0014: ldloca.s 5
IL_0016: call void ConsoleApp1.Program::SomeBigMethod(int32&, int32&, int32&, int32&)
- // (no C# code)
- IL_001b: nop
- // Console.WriteLine(x);
- IL_001c: ldloc.2
- IL_001d: call void [System.Console]System.Console::WriteLine(int32)
- // (no C# code)
- IL_0022: nop
- // Console.WriteLine(result);
- IL_0023: ldloc.0
- IL_0024: call void [System.Console]System.Console::WriteLine(int32)
- // (no C# code)
- IL_0029: nop
- // Console.ReadKey();
- IL_002a: call valuetype [System.Console]System.ConsoleKeyInfo [System.Console]System.Console::ReadKey()
- IL_002f: pop
- // (no C# code)
- IL_0030: ret
- } // end of method Program::Main
- (3)Patterns
作用: 你可以使用 is 运算符来引入一个变量, 这个变量被称为模式变量. 不明白, 看个例子就明白了.
代码如下:
解析: x is string s 的作用是: 如果 x 可以被转换为 string 转换后的值赋值给了 s , 所以输出的结果就是字符串的长度.
其中 switch 的声明也支持这种模式, 而且还可以使用 when 子句指定条件, 代码如下:
运行结果:
解析: Foo2(9)传递过来的是 9, 是 int 类型, 所以就进入到第一个 case 子句中, 所以最终输出的结果就是: It is an int !, 这个解释给零分, 下面我们通过 ILSpy 看看这种语法糖到底是什么东东, 如下图所示:
我就不解释了, 大家一看就明白, 是不是想拍下大腿, TM 原来就这么简单!!!
(4)本地方法(Local methods)
作用: A local method is a method declared inside another function. 这里我给出英文, 因为这种方式给出是最准确的, 中文翻译出来就 TM 看不懂了.
运行结果:
解析:
定义了一个本地方法, 返回值类型是 int 传入的参数是 value , 返回值是: value*value*value+i
Cube(2), 调用传入值 2 , 所以最终计算出来的值为 2*2*2+9=17
注意: 本地方法仅对包含函数可见, 并且可以使用包含该本地方法的变量.
ILSpy 反编译的结果:
可以看出在调用 Cube(2), 最终被编译成 Cube(2,ref xx)这样一个方法, 但看不到 <WriteCubes>g__Cube|3_0 方法的内部实现.
(5)c# 6 介绍了方法的 "fat - 箭头" 语法, 可以用在只读, 属性, 运算符和索引器. c# 7 将此扩展到构造函数, 读 / 写属性, 终结器
代码:
ILSPy 代码结果:
(6)对于 c# 7, 可能最显著的改进是显式元组支持
作用: 元组提供了一种简单的方法来存储一组相关值
代码:
运行结果:
解析: var bob = ("Bob", 23); 定义了一个元组, 可以使用 bob.Item1 来访问第一个参数, 可以使用 bob.Item2 来访问第二参数, 但问题来了, 为什么可以这样来访问???
ILSpy 结果:
可以看到, 元组其实是一个 ValueTuple<,>的泛型类型, 其中 string int 是有你的值的类型决定的, 那为什么可以使用 Item1 和 Item2 来访问对应的值呢?
首先 Item1 和 Item2 是人家 ValueTuple<T1, T2> 中定义的, 那为什么我访问 Item1 就是 "Bob", 那是因为在构造函数中, 把 "Bob" 赋值给了 Item1, 所以明白了吧.
另外可以看出元组是一个结构体, 属于值类型的. 讲到这里还没有讲完元组的点, 由于编译器的魔力, 元组元素可以被命名为下面的形式:
ILSpy 结果:
借助于元组, 函数可以返回多个参数, 而不需要借助于 out 参数:
运行结果:
ILSpy 结果:
注意: 元组隐含地支持反解析模式, 因此它们可以很容易地被分解成单个变量. 我们可以重写前面的主方法使 GetFilePosition 返回的元组被分配给两个局部变量: row 和 cloum:
运行结果:
ILSPy 结果:(结果和上面的一样)
好了, 元组就讲到这里, 接下让我们看看如何抛出异常.
(7)抛出异常
功能: 在 C#7 之前, throw 总是要被声明, 现在它可以作为一个表达式出现在一个函数 体中, 而且也可以出现在三元表达式中.
ILSpy 结果:
(8)字符串的插值
直接上代码:
如果要多行显示, 可以这样写:
注意:$ 符一定要在 @符号之前.
ILSpy 结果:
简单我就不多说了, 继续下面的知识点.
(9)异常筛选器(Exception filters)
作用: 允许你在 catch 中应用一个条件.
(10)引用本地变量 Ref Locals
作用: C#7.0 中引入了一个极为重要的点, 借此, 你可以定义一个本地变量, 这个变量引用一个数组中的元素或者对象中的字段.
代码:
注意: Ref Locals 必须是数组中的一个元素, 字段, 或者本地变量, 不能是属性. 它通常与 ref returns 一起使用.
运行结果:
解析: ref int age 标注这个变量时就是一个引用类型的变量.
(11)Ref Returns
作用: 你可以在一个方法中返回一个 ref local, 这种方式被叫做 ref return
代码:
运行结果:
解析: private static ref int GetX() 其实是一个 返回值为 int32&(就是一个标记了内存指针的 INT32 类型)的方法, 也就是返回一个地址, 这样我再修改值后其实就是修改的 x 的值.
ILSpy 结果:
注意: ldsflda int32 : 是把一个静态字段 x 的地址压入到栈中, ret, 然后返回, 在 Main 方法中, 调用上面的方法后, 从栈顶把值取出来, 存储到本地变量列表中索引位置 0 里面.
然后取本地变量中索引位置为 0 的值, 并压入栈中, 注意重点来了, stind.i4 是把 ldc.i4.s 9 值 的地址存储下来, 这样就改变了 x 的值. 所以这个 int32 & 其实就是一个变量的地址, 也就是我们通常所说的指针.
好了讲到这里基本上 C#7.0 新增的功能就讲的差不多了, 后续我会继续补充 C#7.0 新的知识点, 希望对你有帮助! 谢谢.
最后, 欢迎大家加入到我的 C#+.Net Core 英文书籍翻译群, 我会不定期通过博客更新翻译的英文资料, 希望得到最新的 C# 知识, 同时对你我也有所提高.
参考书籍:C 7.0 in a Nutshell 7th Edition
来源: https://www.cnblogs.com/runningsmallguo/p/8972678.html