AI智能
改变未来

C#9.0 终于来了,您还学的动吗? 带上VS一起解读吧!


一:背景

1. 讲故事

好消息,

.NET 5.0

 终于在2020年6月10日发布了第五个预览版,眼尖的同学一定看到了在这个版本中终于支持了 

C# 9.0

,此处有掌声,太好了!!!

.Net5官方链接

可以看到目前的C#9还是预览版,实现了一部分新语法供开发者提前尝鲜,从github的roslyn仓库上可以看到目前准备实现 

17

个新特性,现阶段已经实现了

8

个,其中的 

In Progress

 表示正在开发中。

新特性预览

2. 安装必备

  • 下载最新的

    net5 sdk

    吧:dotnet-sdk-5.0.100-preview.5.20279.10-win-x64.exe

  • 下载最新的 visual studio 2019 preview 2

找好你自己的vs版本类型哦。。。

二:新特性研究

1. Target-typed new

这个取名一定要留给学易经的大师傅,没见过世面的我不敢造次,取得不佳影响时运,所谓 

运去金成铁, 时来铁似金

 ,不过大概意思就是说直接new你定义的局部变量的类型,用

issues

中总结的话就是:


Summary: Allow Point p = new (x, y);
Shipped in preview in 16.7p1.

接下来就是全部代码,看看

使用前

 和 

使用后

 的具体差别。


   class Program
   {
       static void Main(string[] args)
       {
           //老语法
           var person = new Person(\"mary\", \"123456\");

           //新语法
           Person person2 = new(\"mary\", \"123456\");

           Console.WriteLine($\"person={person}person2={person2}\");
       }
   }

   public class Person
   {
       private string username;
       private string password;

       public Person(string username, string password)
       {
           this.username = username;
           this.password = password;
       }

       public override string ToString()
       {
           return $\"username={username},password={password} \\n\";
       }
   }

然后用ilspy去看看下面的il代码,是不是省略了Person,让自己心里踏实一点。

总的来说这语法还行吧,能起到延长键盘使用寿命的功效。

2. Lambda discard parameters

从字面上看大概就是说可以在lambda上使用取消参数,听起来怪怪的,那本意是什么呢?有时候lambda上的匿名方法签名的参数是不需要的,但在以前必须实打实的定义,这样就会污染方法体,也就是可以在body中被访问,如下图:

但有时候因为客观原因必须使用

Func<int,int,int>

这样的委托,而且还不想让方法签名的参数污染方法体,我猜测在函数式编程中有这样的场景吧,可能有点类似MVC中的

EmptyResult

效果。

好了,我想你大概知道啥意思了,接下来实操一把。。。


   Func<int, int, int> func = (_, _) =>
   {
       return 0;
   };

   var result = func(10, 20);

从图中可以看到,我在方法体中是找不到所谓的 

_

 变量的,这就神奇了,怎么做到的呢?带着这个好奇心看看它的IL代码是个什么样子。


.method private hidebysig static
   void Main (
       string[] args
   ) cil managed
{
   // Method begins at RVA 0x2048
   // Code size 45 (0x2d)
   .maxstack 3
   .entrypoint
   .locals init (
       [0] class [System.Runtime]System.Func`3<int32, int32, int32> func,
       [1] int32 result
   )

   IL_0000: nop
   IL_0001: ldsfld class [System.Runtime]System.Func`3<int32, int32, int32> ConsoleApp1.Program/\'<>c\'::\'<>9__0_0\'
   IL_0006: dup
   IL_0007: brtrue.s IL_0020

   IL_0009: pop
   IL_000a: ldsfld class ConsoleApp1.Program/\'<>c\' ConsoleApp1.Program/\'<>c\'::\'<>9\'
   IL_000f: ldftn instance int32 ConsoleApp1.Program/\'<>c\'::\'<Main>b__0_0\'(int32, int32)
   IL_0015: newobj instance void class [System.Runtime]System.Func`3<int32, int32, int32>::.ctor(object, native int)
   IL_001a: dup
   IL_001b: stsfld class [System.Runtime]System.Func`3<int32, int32, int32> ConsoleApp1.Program/\'<>c\'::\'<>9__0_0\'

   IL_0020: stloc.0
   IL_0021: ldloc.0
   IL_0022: ldc.i4.s 10
   IL_0024: ldc.i4.s 20
   IL_0026: callvirt instance !2 class [System.Runtime]System.Func`3<int32, int32, int32>::Invoke(!0, !1)
   IL_002b: stloc.1
   IL_002c: ret
} // end of method Program::Main

从上面的IL代码来看 匿名方法 变成了

<>c

类的

<Main>b__0_0

方法,完整签名: 

ConsoleApp1.Program/\'<>c\'::\'<Main>b__0_0\'(int32, int32)

,然后再找一下 

<Main>b__0_0

 方法的定义。


.class nested private auto ansi sealed serializable beforefieldinit \'<>c\'
   extends [System.Runtime]System.Object
   .method assembly hidebysig
       instance int32 \'<Main>b__0_0\' (
           int32 _,
           int32 _
       ) cil managed
   {
       // Method begins at RVA 0x2100
       // Code size 7 (0x7)
       .maxstack 1
       .locals init (
           [0] int32
       )

       IL_0000: nop
       IL_0001: ldc.i4.0
       IL_0002: stloc.0
       IL_0003: br.s IL_0005

       IL_0005: ldloc.0
       IL_0006: ret
   } // end of method \'<>c\'::\'<Main>b__0_0\'

这说明什么呢?说明两个参数是真实存在的,但编译器捣了鬼,做了语法上的限制,不让你访问所谓的 

_

等等。。。有一个问题,IL中的方法签名怎么是这样的: 

<Main>b__0_0 (int32 _,int32 _)

, 大家应该知道方法签名中不可以出现重复的参数名,比如下面这样定义肯定是报错的。

这说明什么?说明这个语法糖不仅需要编译器支持,更需要底层的JIT支持,那怎么证明呢?我们用windbg去底层挖一挖。。。为了方便调试,修改如下:


       static void Main(string[] args)
       {
           Func<int, int, int> func = (_, _) =>
           {
               Console.WriteLine(\"进入方法体了!!!\");
               Console.ReadLine();
               return 0;
           };

           var result = func(10, 20);
       }

0:000> !clrstack -p
OS Thread Id: 0x52e8 (0)
0000007035F7E5C0 00007ffaff362655 ConsoleApp1.Program+c.b__0_0(Int32, Int32) [C:\\5\\ConsoleApp1\\ConsoleApp1\\Program.cs @ 13]
   PARAMETERS:
       this (0x0000007035F7E600) = 0x000001968000cb48
       _ (0x0000007035F7E608) = 0x000000000000000a
       _ (0x0000007035F7E610) = 0x0000000000000014

从图中可以看到,虽然都是 

_

 ,但在线程栈上是完完全全的两个栈地址。 

0x0000007035F7E608

 和 

0x0000007035F7E610

三:总结

总的来说,C#是越来越像函数式编程靠拢,越来越像Scala,就像Jquery的口号一样:Write less,do more。

好了,先就说这两个吧,大家先安装好工具,明天继续解剖~~~

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » C#9.0 终于来了,您还学的动吗? 带上VS一起解读吧!