Until C# 9.0, all executable code in C# was required to appear within a type definition and a member such as a method within a type. As we saw in Listing 1.1, this is no longer required. Instead, it is possible for a single file to have top-level statements, statements that appear independent of any type definition and even without a Main method. Such statements are allowed in only one file, and they will be the first statements to execute within the program—the equivalent of the many statements that appear in the Main method. In fact, given top-level statements, the compiler generates a class called Program that wraps the top-level statements and places them into a Main method. Furthermore, the method has a contextual keyword—args—that is the equivalent of the string args parameter of a Main method.
With top-level statements, C# syntax allows the statements outside a method as a simplification—especially for new C# developers familiar with other languages that are less structured—but then moves these statements into the Main method at compiler time. The end result, therefore, is that there are never any statements in the underlying CIL that are not placed within a type definition and within a type member.
Since the C# compiler moves top-level methods into its own generated main method that is defined within a class named Program, the compiler will issue an error if you try to define an additional class called Program.5 One additional restriction on top-level statements is that any type definition within the same file as the top-level statements must appear after such statements.
The file with top-level statements can also contain methods, which we will call top-level methods, and such methods can optionally appear independent of a type definition as well.
When running some of the New Project wizards in Visual Studio, there is frequently an option called “Do not use top-level statements” that allows you to choose whether to go with the simpler version or to generate the structure explicitly, resulting in code that looks more like Listing 1.1 (without a class or Main method). Similarly, on the dotnet command line, some of the project templates (i.e., the Console argument when running the dotnet new Console command) frequently have a --use-program-main option. However, top-level statements are available to only those projects that have entry points, in other words, Main methods. The compiler doesn’t allow top-level statements on programs that do not have Main methods (such as class libraries).
Top-level statements were mainly introduced to remove unnecessary ceremony when writing simple programs, reducing the complexity for beginners especially. Prior to top-level statements, a program with a single statement would require both a type definition and a Main method just to code the single statement. With top-level statements, this structure is optionally eliminated. In addition, top-level statements allow for easier embedding of seemingly complete C# snippets within text like Polyglot notebooks (https://github.com/dotnet/interactive) or the online version of this book (https://essentialcsharp.com).