一:背景

1. 讲故事

.NET 5 终于在 6月25日 发布了第六个预览版,随之而来的是更多的新特性加入到了 C# 9 Preview 中,这个系列也可以继续往下写了,废话不多说,今天来看一下 Top-level programsExtending Partial Methods 两大新特性。

2. 安装必备

下载最新的 .net 5 preview 6

下载最新的 Visual Studio 2019 version 16.7 Preview 3.1

二:新特性研究

1. Top-level programs

如果大家玩过 python,应该知道在 xxx.py 中写一句 print,这程序就能跑起来了,简单高效又粗暴,很开心的是这特性被带到了C# 9.0 中。

  • 修改前

using System; namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
  • 修改后

System.Console.WriteLine("Hello World!");

这就有意思了,Main入口函数去哪了? 没它的话,JIT还怎么编译代码呢? 想知道答案的话用 ILSpy 反编译看一下就好啦!


.class private auto ansi abstract sealed beforefieldinit $Program
extends [System.Runtime]System.Object
{
.custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Methods
.method private hidebysig static
void $Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Code size 18 (0x12)
.maxstack 8
.entrypoint IL_0000: ldstr "Hello World!"
IL_0005: call void [System.Console]System.Console::WriteLine(string)
IL_000a: nop
IL_000b: call string [System.Console]System.Console::ReadLine()
IL_0010: pop
IL_0011: ret
} // end of method $Program::$Main } // end of class $Program

从 IL 上看,类变成了 $Program, 入口方法变成了 $Main, 这就好玩了,在我们的印象中入口函数必须是 Main,否则编译器会给你一个大大的错误,你加了一个 $ 符号,那CLR还能认识吗? 能不能认识我们用 windbg 看一些托管和非托管堆栈,看看有什么新发现。


0:010> ~0s
ntdll!NtReadFile+0x14:
00007ffe`f8f8aa64 c3 ret
0:000> !dumpstack
OS Thread Id: 0x7278 (0)
Current frame: ntdll!NtReadFile + 0x14
Child-SP RetAddr Caller, Callee
0000008551F7E810 00007ffed1e841dc (MethodDesc 00007ffe4020d500 + 0x1c System.Console.ReadLine()), calling 00007ffe400ab090
0000008551F7E840 00007ffe4014244a (MethodDesc 00007ffe401e58f0 + 0x3a $Program.$Main(System.String[])), calling 00007ffe40240f58
0000008551F7E880 00007ffe9fcc8b43 coreclr!CallDescrWorkerInternal + 0x83 [F:\workspace\_work\1\s\src\coreclr\src\vm\amd64\CallDescrWorkerAMD64.asm:101]
0000008551F7E8C0 00007ffe9fbd1e03 coreclr!MethodDescCallSite::CallTargetWorker + 0x263 [F:\workspace\_work\1\s\src\coreclr\src\vm\callhelpers.cpp:554], calling coreclr!CallDescrWorkerWithHandler [F:\workspace\_work\1\s\src\coreclr\src\vm\callhelpers.cpp:56]
0000008551F7E950 00007ffe9fb8c4e5 coreclr!MethodDesc::IsVoid + 0x21 [F:\workspace\_work\1\s\src\coreclr\src\vm\method.cpp:1098], calling coreclr!MetaSig::IsReturnTypeVoid [F:\workspace\_work\1\s\src\coreclr\src\vm\siginfo.cpp:5189]
0000008551F7EA00 00007ffe9fb8c4bf coreclr!RunMainInternal + 0x11f [F:\workspace\_work\1\s\src\coreclr\src\vm\assembly.cpp:1488], calling coreclr!MethodDescCallSite::CallTargetWorker [F:\workspace\_work\1\s\src\coreclr\src\vm\callhelpers.cpp:266]
0000008551F7EB30 00007ffe9fb8c30a coreclr!RunMain + 0xd2 [F:\workspace\_work\1\s\src\coreclr\src\vm\assembly.cpp:1559], calling coreclr!RunMainInternal [F:\workspace\_work\1\s\src\coreclr\src\vm\assembly.cpp:1459]

从上面堆栈的流程图看: coreclr!RunMain -> coreclr!MethodDesc -> coreclr!CallDescrWorkerInternal -> $Program.$Main, 确实被调用了,不过有一个重大发现,在 $Program.$Main 调用之前底层的 CLR 读取了 方法描述符,这就是一个重大突破点,方法描述符在哪里呢? 可以用 ildasm 去看一下元数据列表。

可以看到,入口函数那里打上了一个 ENTRYPOINT 标记,这就说明入口函数名其实是可以随便更改的,只要被 ENTRYPOINT打上标记即可,CoreCLR就能认的出来~~~

2. Partial Methods

我们知道 部分方法 是一个很好的桩函数,而且在 C# 3.0 中就已经实现了,那时候给我们增加了很多限制,如下图:

翻译过来就是:

  • 部分方法的签名必须一致

  • 方法必须返回void

  • 不允许使用访问修饰符,而且还是隐式私有的。

在 C# 9.0 中放开了对 方法签名 的所有限制,正如 issue 总结:

这是一个非常好的消息,现在你的部分方法上可以加上各种类型的返回值啦,这里我举一个例子:


class Program
{
static void Main(string[] args)
{
var person = new Person(); Console.WriteLine(person.Run("jack"));
}
} public partial class Person
{
public partial string Run(string name);
} public partial class Person
{
public partial string Run(string name) => $"{name}:开溜了~";
}

然后我们用 ILSpy 简单看看底层怎么玩的,如下图可以看到其实就是一个简单的合成,对吧。

现在我有想法了,如果我不给 Run 方法实现会怎么样? 把下面的 partial 类注释掉看一下。

从报错信息看,可访问的修饰符必须要有方法实现,还以为直接编译的时候抹掉呢。 这就起不到桩函数的作用:-D,不过这个特性还是给了我们更多的可能用的到的应用场景吧。

三:总结

本篇两个特性还是非常实用的,Top-level programs 让我们可以写更少的代码,甚至拿起 记事本 都可以快捷的编写类似一次性使用的测试代码, Partial Methods 特性留给大家补充吧,我基本上算是没用过 (┬_┬)。

如您有更多问题与我互动,扫描下方进来吧~

C# 9.0 终于来了, Top-level programs 和 Partial Methods 两大新特性探究的更多相关文章

  1. C#9.0 终于来了,带你一起解读Pattern matching 和 nint 两大新特性玩法

    一:背景 1. 讲故事 上一篇跟大家聊到了Target-typed new 和 Lambda discard parameters,看博客园和公号里的阅读量都达到了新高,甚是欣慰,不管大家对新特性是多 ...

  2. 有史来最大改变 Android 5.0十大新特性

    有史来最大改变 Android 5.0十大新特性 2014.10.16 14:51:31 来源:腾讯数码作者:腾讯数码 ( 0 条评论 )   距离Android系统上一次重大更新不到一年的时间,谷歌 ...

  3. Android 5.0(棒棒糖))十大新特性

    Android 5.0(棒棒糖))十大新特性 1. 全新Material Design设计风格 Android Lollipop全新的设计语言是受到了多种因素影响,是一种大胆的平面化创新.换句话说,谷 ...

  4. [Android 新特性] 有史来最大改变 Android 5.0十大新特性

    距离Android系统上一次重大更新不到一年的时间,谷歌再一次从KitKat升级到了Lollipop,而两次都使用糖果来命名,营销的目的显露无 遗.当我们首次看到Android 5.0 Lollipo ...

  5. Java 14 令人期待的 5 大新特性,打包工具终于要来了

    随着新的 Java 发布生命周期的到来,新版本预计将于 2020 年 3 月发布,本文将对其中的 5 个主要特性作些概述. Java 13刚刚发布给开发人员使用不久,最新版本的JDK于2019年9月发 ...

  6. jQuery 3.0最终版发布,十大新特性眼前一亮

    jQuery 3.0在日前发布了最终的全新版本.从2014年10月,jQuery团队对这个主要大版本进行维护开始,web开发者社区便一直在期待着这一刻的到来,终于在2016年6月他们迎来了这一个最终板 ...

  7. C#6.0中10大新特性的应用和总结

    微软发布C#6.0.VS2015等系列产品也有一段时间了,但是网上的教程却不多,这里真对C#6.0给大家做了一些示例,分享给大家.   微软于2015年7月21日发布了Visual Studio 20 ...

  8. MySQL 8.0的十大新特性

    今天,让我们看一下MySQL8.0提升数据库管理员工作效率的十大改进. 从一大堆特性你们找出十点并不太容易,以下是这十大特性: 1.临时表的改进 2.持续的全局变量 3.取消默认MyISAM系统表 4 ...

  9. FineUI(专业版)v2.6.0即将支持的两个新特性!

    特性1:以一挡三,将 160 行代码缩减为 60 行的技巧! 为了更新单元格的编辑值,我们需要下面三个函数同时上阵: GetModifiedDict:修改的单元格值 GetDeletedList:删除 ...

随机推荐

  1. 快速复习C语言 - 1变量与运算符

    变量与运算符 本篇以读者知道 int.char.float.double 等数据类型为前提条件. float 类型注意事项 float 类型数没有办法跟一个数真正比较是否相等,可以定义借助绝对值在一定 ...

  2. Rocket - tilelink - RegionReplicator

    https://mp.weixin.qq.com/s/XZVCdt50tM6lavchGm9GRg   简单介绍RegionReplicator的实现.   ​​   1. 基本介绍   根据mask ...

  3. jchdl - GSL实例:HalfAdder

    https://mp.weixin.qq.com/s/Y97bIro7UlPPFCoPlzgmOQ 半加器电路是指对两个输入相加,输出一个结果位和,没有进位输入的电路. 是实现两个一位二进制数的加法运 ...

  4. doReleaseShared源码分析及唤醒后继节点的过程分析

    文章结构 源码:对doReleaseShared()方法的源码进行一些注释 使用场景:介绍doReleaseShared()使用位置,及目的 以写锁开始的队列:分析写锁开始得同步等待队列在唤醒后续读锁 ...

  5. Java实现 LeetCode 726 原子的数量(递归+HashMap处理)

    726. 原子的数量 给定一个化学式formula(作为字符串),返回每种原子的数量. 原子总是以一个大写字母开始,接着跟随0个或任意个小写字母,表示原子的名字. 如果数量大于 1,原子后会跟着数字表 ...

  6. Java实现 蓝桥杯VIP 算法训练 数的划分

    [题目描述] 将整数n分成k份,且每份不能为空,任意两份不能相同(不考虑顺序). 例如:n=7,k=3,下面三种分法被认为是相同的. 1,1,5: 1,5,1: 5,1,1: 问有多少种不同的分法. ...

  7. Java实现 LeetCode 275 H指数 II

    275. H指数 II 给定一位研究者论文被引用次数的数组(被引用次数是非负整数),数组已经按照升序排列.编写一个方法,计算出研究者的 h 指数. h 指数的定义: "h 代表"高 ...

  8. Java实现 LeetCode 198 打家劫舍

    198. 打家劫舍 你是一个专业的小偷,计划偷窃沿街的房屋.每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报 ...

  9. Java实现 蓝桥杯 算法提高 数组求和

    试题 算法提高 数组求和 资源限制 时间限制:1.0s 内存限制:256.0MB 问题描述 输入n个数,围成一圈,求连续m(m<n)个数的和最大为多少? 输入格式 输入的第一行包含两个整数n, ...

  10. python—socket编程

    一:客户端/服务器 架构 1.硬件C/S架构:(例如,打印机) 2.软件C/S架构:互联网中处处是C/S架构 腾讯作为服务端为你提供视频,你得下个腾讯视频客户端才能看它的视频 C/S架构与socket ...