C# 9.0 终于来了, Top-level programs 和 Partial Methods 两大新特性探究

一:背景

1. 讲故事

.NET 5 终于在 6月25日 发布了第六个预览版,随之而来的是更多的新特性加入到了 C# 9 Preview 中,这个系列也可以继续往下写了,废话不多说,今天来看一下 Top-level programs 和 Extending 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 $Programextends [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.entrypointIL_0000: ldstr "Hello World!"IL_0005: call void [System.Console]System.Console::WriteLine(string)IL_000a: nopIL_000b: call string [System.Console]System.Console::ReadLine()IL_0010: popIL_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 特性留给大家补充吧,我基本上算是没用过 (┬_┬)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/309074.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4位BCD计数器设计

状态转换: 输出函数: 4位BCD计数器设计:

[Mvp.Blazor] 集成Ids4,实现统一授权认证

(又一个客户端集成了IdentityServer4)还是很开心的,目前已经有六个开源项目都集成到了Ids4认证中心了。1、Blazor系列文章回顾书接上文,关于Blazor学习呢,我也发了几篇文章了,我一般写东西都喜欢偏实战&…

重学ASP.NET Core 中的标记帮助程序

标记帮助程序是什么标记帮助程序使服务器端代码可以在 Razor 文件中参与创建和呈现 HTML 元素。 例如,内置的 ImageTagHelper 可以将版本号追加到图片名称。 每当图片发生变化时,服务器都会为图像生成一个新的唯一版本号,因此客户端总能获得…

小型数字系统---运动码表设计

运动码表设计中码表控制器是最难的,所以主要介绍如何设计码表控制器。 我们先给这个时序电路设计状态以及编码 先设计状态转换函数 首先画出状态图: (其中我们让其一到000状态就立马变为001状态,110状态存储完数据立马变成100状态) 根据状态图&#…

用 aforge.net 小试一下验证码识别

今天来小玩一下 aforge.net 套用官方的话就是一个专门为开发者和研究者基于C#框架设计的,这个框架提供了不同的类库和关于类库的资源,还有很多应用程序例子,包括计算机视觉与人工智能,图像处理,神经网络,遗…

国标转区位码电路设计及汉字显示

区位码国际码-2020H,由于采用加法器实现,因此用补码形式进行表示,区位码 国际码FFFF-2020H0001H国际码dfe0。 从网上找下机内码转化的软件,将一段自选文字输入该软件,进行十六进制的机内码转化。由于一个汉字是由四个十…

【WPF】DataGrid多表头的样式设计

需求在使用WPF开发时,使用DataGrid列表显示数据时,有些字段可以进行分组显示,用于更好的表达它们之间存在的某种关系,因此就考虑到要对DataGrid的表头进行扩展,可以显示多行表头,让这些有关联的字段内容显示…

异步清零和同步置数/清零的区别

同步,异步是对于时钟而言的。 同步指的是,状态的变化需要等待时钟有效沿来触发,所有动作同时跟随这个时钟变化,而异步时,状态变化不依赖与时钟。 异步清零说的是,你需要对一个计数器在满足某种条件时想要…

如何使用ABP进行软件开发之基础概览

ABP框架简述1)简介在.NET众多的技术框架中,ABP框架(本系列中指aspnetboilerplate项目)以其独特的魅力吸引了一群优秀开发者广泛的使用。在该框架的赋能之下,开发者可根据需求通过官方网站【https://aspnetboilerplate.…

模2加法,模2减法,模2除法

十进制的除法,大家都会做:列个竖式,商,写在上面,上个几,再用被除数减去积,求得余数…。 二进制的除法,和十进制的计算方法相同,也要列出个竖式计算。 二进制的除法&…

《Unit Testing》2.1 伦敦学派如何做隔离

针对单元测试的定义,主要有两种看法:经典学派。经典学派之所以经典,是因为这原本就是人们做单元测试和测试驱动开发的方式伦敦学派。伦敦学派扎根于伦敦的编程社区。单元测试的定义单元测试有很多定义,但是所有的定义都有三个重要…

找出第i个小元素(算法导论第三版9.2-4题)

找出第i个小元素(算法导论第三版9.2-4题) 期望时间复杂度:Θ(n) 最坏情况的时间复杂度Θ(n^2) int randomized_select_based_loop(int *array,int start,int end,int index) {while (true){if(start end)return array[start];int middle randomized_partition(a…

C++实现数组模拟链表(实现链表的增删功能)

代码如下: #include <iostream> using namespace std; const int N 100;struct Node {int data;int next; };class ArrayList { private:Node node[N];int maxSize;//数组容量int idx;//接下来要插入的结点的下标int len;//链表长度public:void initList(){node[0].nex…

Gartner:容器采用将迅速增长,但不会很快有利可图

导语容器的未来可期&#xff0c;到2024年&#xff0c;所有应用程序中的15&#xff05;将在容器中运行&#xff0c;而这一数据今天仅为5&#xff05;&#xff0c;但当前变现还比较难。正文近日&#xff0c;Gartner公司发表其首次为软件容器管理软件和服务市场做的预测。Gartner说…

快速排序在最坏的情况下时间复杂度(Ω(nlgn)(算法导论第三版9.3-3))

快速排序在最坏的情况下时间复杂度Ω(nlgn) 1⃣️在元素各异时或者少量相等&#xff08;元素个数n>70) 时间复杂度Ω(nlgn) void quick_sort_by_median(int *array,int start,int end) {if(start<end){int median select(array,start,end,(end - start 1)/2 (end - …

线性表的定义与操作-顺序表,链式表(C语言)

顺序表: typedef int Position; typedef struct LNode *List; struct LNode {ElementType Data[MAXSIZE];Position Last; };/* 初始化 */ List MakeEmpty() {List L;L (List)malloc(sizeof(struct LNode));L->Last -1;return L; }/* 查找 */ #define ERROR -1Position Fi…

【Azure Show】|第三期 人工智能大咖与您分享!嘉宾陈海平胡浩陈堰平

欢迎来到Azure Show!Azure ShowHello,大家好&#xff0c;又来到新的一期的Azure Show!本期是人工智能专场&#xff0c;我们邀请到微软Data&AI的解决方案架构师陈堰平&#xff0c;Tensorflow.NET 作者陈海平&#xff0c;还有微软人工智能方向最有价值专家胡浩和大家分享人工…

确定S中最接近中位数的k个元素(算法导论第三版9.3-7)

确定S中最接近中位数的k个元素 &#xff08;算法导论第三版9.3-7题&#xff09; 时间复杂度O(n) vector<int> k_elements_closest_to_median(int *array,int start,int end,int k) {vector<int> result;int median select(array,start,end,(end - start 1)/2 …

深入探究ASP.NET Core异常处理中间件

前言全局异常处理是我们编程过程中不可或缺的重要环节。有了全局异常处理机制给我们带来了很多便捷&#xff0c;首先我们不用满屏幕处理程序可能出现的异常&#xff0c;其次我们可以对异常进行统一的处理&#xff0c;比如收集异常信息或者返回统一的格式等等。ASP.NET Core为我…