Introduced in C# 9.0, Top-level statements allow you to omit the explicit entry point (Main method) that was traditionally required. This enables you to start programs simply, much like scripting languages, and significantly reduces boilerplate code, especially for small tools or learning purposes.
In this article, I will explain the specific usage and specifications of top-level statements while comparing them with the traditional writing style.
Comparison with Traditional Syntax
Let’s compare the two styles using a simple program that displays the current time when the application starts.
Traditional Style (C# 8.0 and earlier)
Previously, it was necessary to define class Program and static void Main as the program’s entry point. This resulted in deep nesting and required a lot of code even for a few lines of processing.
using System;
namespace TimeDisplayApp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine($"Current time: {DateTime.Now}");
}
}
}
Top-level Statements (C# 9.0 and later)
With top-level statements, you can omit the definitions for the namespace, class, and Main method. You simply write the processing you want to execute starting from the top of the file.
using System;
// Main method and class definition are unnecessary
Console.WriteLine($"Current time: {DateTime.Now}");
// You can also return a value (int exit code)
return 0;
Since the compiler generates the Main method internally, the underlying behavior is exactly the same as the traditional method.
Features and Specifications
Top-level statements are not just about writing less code; they also implicitly support the features necessary for an entry point.
Using Command Line Arguments (args)
Even without defining Main(string[] args), a variable named args is implicitly available. Command line arguments are stored in this variable.
using System;
if (args.Length > 0)
{
foreach (var arg in args)
{
Console.WriteLine($"Argument: {arg}");
}
}
else
{
Console.WriteLine("No arguments specified.");
}
Defining and Calling Methods
Within a file using top-level statements, you can easily define and call methods, similar to local functions. These methods are written after the top-level statements.
using System;
var message = "Starting system check.";
Log(message);
// Execute process
await Task.Delay(1000); // Asynchronous processing can be written directly
Log("Completed.");
// Define methods at the bottom of the file
void Log(string text)
{
Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {text}");
}
Important Notes and Constraints
When using top-level statements, you must adhere to a few rules.
- Only one file per project A file containing top-level statements can only exist once per project. Since the compiler generates the entry point, having multiple files with top-level statements will cause a conflict.
- Order of writing
usingdirectives must be written at the beginning of the file, followed by the top-level statements (execution code). Class or namespace definitions must be written after all top-level statements.
using System;
// 1. Execution code (Top-level statements)
Console.WriteLine("Hello");
// 2. Class definition (Must be written AFTER the execution code)
public class MyHelper
{
public void DoSomething() { }
}
Summary
By using top-level statements, you can reduce the amount of C# code and focus more on the essential logic.
- You can omit the boilerplate for
class Programand theMainmethod. - Command line arguments are accessible via the
argsvariable. - Asynchronous processing using
awaitcan be written intuitively.
This feature is particularly useful for creating console applications and prototyping functions. Use it actively to make your code cleaner and more efficient.
