Finally, C # 9.0 is coming, exploring the two new features of top level programs and partial methods


1: Background

1. Tell a story

. net 5 finally released its sixth preview on June 25, followed by more new features added to the C # 9 preview. This series can also continue to write. No more nonsense. Let’s take a look todayTop-level programsandExtending Partial MethodsTwo new features.

2. Installation requirements

Download the 5 preview 6

Download the latest Visual Studio 2019 version 16.7 Preview 3.1

2: Research on new characteristics

1. Top-level programs

If you’ve ever played python, you should know This program can run easily, efficiently and rudely. I’m glad that this feature has been brought into C # 9.0.

  • Before revision
using System;

namespace ConsoleApp2
    class Program
        static void Main(string[] args)
            Console.WriteLine("Hello World!");
  • After modification
System.Console.WriteLine("Hello World!");

That’s interesting. Where’s the main entry function? How can JIT compile code without it? If you want to know the answer, just decompile it with 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

		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

In terms of IL, classes become$ProgramThe entry method becomes$MainThat’s funny. In our impression, the entry function must beMainOtherwise, the compiler will give you a big error. If you add a $symbol, can CLR recognize it? Can you see if we can use WinDbg to look at some managed and unmanaged stacks and see what we find.

0:010> ~0s
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\s\src\coreclr\src\vm\amd64\CallDescrWorkerAMD64.asm:101]
0000008551F7E8C0 00007ffe9fbd1e03 coreclr!MethodDescCallSite::CallTargetWorker + 0x263 [F:\workspace\_work\s\src\coreclr\src\vm\callhelpers.cpp:554], calling coreclr!CallDescrWorkerWithHandler [F:\workspace\_work\s\src\coreclr\src\vm\callhelpers.cpp:56]
0000008551F7E950 00007ffe9fb8c4e5 coreclr!MethodDesc::IsVoid + 0x21 [F:\workspace\_work\s\src\coreclr\src\vm\method.cpp:1098], calling coreclr!MetaSig::IsReturnTypeVoid [F:\workspace\_work\s\src\coreclr\src\vm\siginfo.cpp:5189]
0000008551F7EA00 00007ffe9fb8c4bf coreclr!RunMainInternal + 0x11f [F:\workspace\_work\s\src\coreclr\src\vm\assembly.cpp:1488], calling coreclr!MethodDescCallSite::CallTargetWorker [F:\workspace\_work\s\src\coreclr\src\vm\callhelpers.cpp:266]
0000008551F7EB30 00007ffe9fb8c30a coreclr!RunMain + 0xd2 [F:\workspace\_work\s\src\coreclr\src\vm\assembly.cpp:1559], calling coreclr!RunMainInternal [F:\workspace\_work\s\src\coreclr\src\vm\assembly.cpp:1459]

From the flow chart of the stack above:coreclr!RunMain -> coreclr!MethodDesc -> coreclr!CallDescrWorkerInternal -> $Program.$MainIt’s called, but there’s a big discovery in$Program.$MainBefore calling, the underlying CLR reads the method descriptor, which is a major breakthrough. Where is the method descriptor? You can use ILdasm to look at the metadata list.

As you can see, the entry function is marked with aENTRYPOINTThis means that the entry function name can be changed as long as it is markedENTRYPOINTMark it, coreclr will recognize it~~~

2. Partial Methods

We know that some methods are good pile functions, and they have been implemented in C # 3.0. At that time, many restrictions were added to us, as shown in the following figure:

The translation is:

  • The signatures of some methods must be consistent

  • Method must return void

  • Access modifiers are not allowed and are implicitly private.

In C # 9.0, all the restrictions on method signature are released. As issue concludes:

This is very good news. Now you can add various types of return values to some of your methods. Here is an example:

class Program
        static void Main(string[] args)
            var person = new Person();


    public partial class Person
        public partial string Run(string name);

    public partial class Person
        ">" (public name) = {string ~};

Then we use ilspy to simply see how the bottom layer plays. As shown in the figure below, it’s actually a simple synthesis, right.

Now I have an idea. What if I don’t implement the run method? Take a look at the partial class below.

From the perspective of error information, accessible modifiers must be implemented in a way,I thought it would be erased when compiling directly.This does not play the role of pile function: – D, but this feature still gives us more possible application scenarios.

3: Summary

The two features in this article are very practical. Top level programs allow us to write less code, and even pick up Notepad to quickly write test code similar to one-time use. Let’s add the partial methods feature. I haven’t used it (┬).

If you have more questions to interact with me, please come in~


Recommended Today

Libp2p RS version 0.3.0 introduction

V0.3.0 released on 4.23, usingAsyncRead & AsyncWriteTo replace ourReadEx & WriteEx & SplitEx; SimplifiedKad/DHTImplementation logic. modify ReadEx & WriteEx & SplitEx: At first we tried to useasync-traitTo define their own IO operationsTraitFor more pure useasync/awaitTo write code. withReadExFor example, it is roughly as follows: #[async_trait] pub trait ReadEx { async fn read(&mut self, buf: &mut […]