C# 9.0 new features Preview – top level statement

Time:2021-12-28

C# 9.0 new features Preview – top level statement

preface

Follow Net 5 is approaching the release date. The corresponding c# new version has been determined as c# 9.0, and the newly added features (or syntax sugar) have been basically locked. This series of articles will show you them.

catalogue

[C# 9.0 new features Preview – new for type derivation]
[C# 9.0 preview of new features – null parameter verification]
[c# 9.0 new features Preview – top level statement]
[c# 9.0 new features Preview – record type]
[preview of c# 9.0 new features – improvement of pattern matching]
[c# 9.0 new features Preview – source code generator]
[c# 9.0 new features Preview – other small changes]

Top level statements

The name of top-level statement doesn’t seem so intuitive. Perhaps its former name is better: simple programs.

objective

As we all know, even the simplest c# program will have a certain amount of red tape, because at least one main method is required. This seems to hinder language learning and program clarity. Therefore, the main purpose of this feature is to make it easier to write c# programs for beginners and code clarity.

grammar

The syntax spec is as follows. It is allowed to add a group of statements before the namespace declaration, and only one compilation unit (which can be considered as a source file) is allowed to have such statements:

compilation_unit
    : extern_alias_directive* using_directive* global_attributes? statement* namespace_member_declaration*
    ;

Spec is difficult to understand. Let’s look at the example directly: in short, it allows you to write code statements directly in the source file without writing the main method:

System.Console.WriteLine("Hi!");

The above code will be translated as:

static class $Program
{
    static void $Main(string[] args)
    {
        System.Console.WriteLine("Hi!");
    }
}

You can see that the writeline statement is automatically wrapped in a class and main method.
The return value of the automatically generated main method will also change according to whether it is asynchronous and whether there is a return value, for example:

await System.Threading.Tasks.Task.Delay(1000);
System.Console.WriteLine("Hi!");
return 0;

Will be translated as:

static class $Program
{
    static async Task $Main(string[] args)
    {
        await System.Threading.Tasks.Task.Delay(1000);
        System.Console.WriteLine("Hi!");
        return 0;
    }
}

Various scenes

  • Support after using statement:
using System;

Console.Write("Hi!");

Will be translated as:

using System;

static class $Program
{
    static void $Main(string[] args)
    {
        Console.Write("Hi!");
    }
}
  • You can also add local functions:
local();
void local() => System.Console.WriteLine(2);
  • Can coexist with other code, such as class declaration:
Type.M();
static class Type
{
    public static void M()
    {
        System.Console.WriteLine("Hi!");
    }
}

Slightly more complex:

await using (var x = new C())
{
    System.Console.Write("body ");
}
class C : System.IAsyncDisposable, System.IDisposable
{
    public System.Threading.Tasks.ValueTask DisposeAsync()
    {
        System.Console.Write("DisposeAsync");
        return new System.Threading.Tasks.ValueTask(System.Threading.Tasks.Task.CompletedTask);
    }
    public void Dispose()
    {
        System.Console.Write("IGNORED");
    }
}
  • At the same time, it is compatible with the syntax of using alias
using alias1 = Test;
string Test() => ""1"";
System.Console.WriteLine(Test());
class Test {}
delegate Test D(alias1 x);
namespace N1
{
    using alias2 = Test;
    delegate Test D(alias2 x);
}
  • It can also be declared together with the displayed main method, but the displayed main method will be ignored and a warning will be prompted
using System;
using System.Threading.Tasks;
System.Console.Write("Hi!");
class Program
{
    static void Main() // warning CS7022: The entry point of the program is global code; ignoring 'Program.Main()' entry point
    {
        Console.Write("hello");
    }
}

limit

  • Top level statements under multiple compilation units are not supported:
// file1.cs
System.Console.WriteLine("1"); // error CS9001: Only one compilation unit can have top-level statements.

// file2.cs
System.Console.WriteLine("2"); // error CS9001: Only one compilation unit can have top-level statements.
  • Cannot be placed inside a class
class Test
{
    System.Console.WriteLine("Hi!"); // ERROR
}
  • Cannot be placed inside a namespace
namespace Test
{
    System.Console.WriteLine("Hi!"); // ERROR
}
  • Either all branches have return values or none
System.Console.WriteLine();
if (args.Length == 0)
{
    return 10; //  Error cs0161: not all code branches have return values
}
  • Although it can be written together with the class declaration, the main method args input parameter cannot be called in the class, because it will be compiled into two classes at compile time
System.Console.WriteLine(args);
class Test
{
    void M()
    {
        System.Console.WriteLine(args); // ERROR
    }
}
  • Naturally, you can’t name local functions with args
args(1);
void args(int x) // ERROR
{}

reference resources

[Proposal: Simplified Null Argument Checking]
[Unit test: NullCheckedParameterTests.cs]
[LDM-2019-07-10.md]