A Guide to C# 9 Top-Level Statements

An introduction to C# 9's top-level statements feature, which allows you to write simple programs without the ceremony of a Program class and a Main method. Learn how it simplifies code and makes C# more beginner-friendly.

For years, the simplest "Hello, World!" program in C# has been surprisingly verbose. You needed a Program class and a static void Main method, all just to print a single line to the console. This ceremony could be intimidating for beginners and was unnecessary for small scripts and utilities.

C# 9, released with .NET 5, introduces a feature to solve this: top-level statements.

The Old Way: The Program.cs Boilerplate

Before C# 9, the minimal C# program looked like this:

using System;

namespace HelloWorld
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, World!");
        }
    }
}

All of this code was just boilerplate to get to the one line that actually does something.

The New Way: Top-Level Statements

With C# 9, you can now write that same program in a single line in your Program.cs file:

System.Console.WriteLine("Hello, World!");

That's it. You can just write your statements at the top level of a file, and the C# compiler will generate the Program class and Main method for you behind the scenes.

How Does It Work?

The compiler takes your top-level statements and synthesizes them into the Main method of a generated Program class. This means you can only have one file with top-level statements in your entire project, as a project can only have one entry point.

You can still do everything you could do in a traditional Main method:

Accessing Command-Line Arguments:

The args array is magically available to you.

if (args.Length > 0)
{
    Console.WriteLine($"Hello, {args[0]}!");
}
else
{
    Console.WriteLine("Hello, World!");
}

Returning an Exit Code:

You can return an integer value, which will be used as the process exit code.

return 1; // Indicates an error

Using async/await:

You can also use await, and the compiler will generate an async Task Main method for you.

using System.Net.Http;

var client = new HttpClient();
var response = await client.GetStringAsync("https://www.example.com");
Console.WriteLine(response.Length);

Where Should You Use Top-Level Statements?

This feature is ideal for:

  • Beginners: It makes C# much more approachable for people learning to code, as they don't have to understand classes, methods, and namespaces just to write their first program.
  • Small Utilities and Scripts: It's perfect for writing small console applications and scripts where the boilerplate of a full Program class is unnecessary.
  • Minimal Web APIs: In .NET 6, this feature would become the foundation for the new minimal API syntax, allowing you to create a web API in just a few lines of code.

For larger, more complex console applications, you may still prefer the structure of a traditional Program class with an explicit Main method, but for many use cases, top-level statements provide a much cleaner and more concise starting point.

Conclusion

Top-level statements are a fantastic quality-of-life improvement for the C# language. By removing unnecessary boilerplate, they make the language easier to learn and faster to write for simple applications. It's a perfect example of how C# continues to evolve to meet the needs of modern developers, from large-scale enterprise applications to small, simple scripts.