Skip to main content

C# Top-level Statements

Introduction

Introduced in C# 9.0, top-level statements represent one of the most significant syntactic changes to the C# language in recent years. They allow you to write code directly at the "top level" of a file without explicitly declaring a namespace, class, or Main method. This feature was designed primarily to reduce the amount of boilerplate code needed for simple programs, making C# more approachable for beginners and more efficient for quick scripting tasks.

Before we dive into the details, let's compare the traditional way of writing a C# program with the new top-level statements approach.

Traditional C# Program Structure

Before C# 9.0, even the simplest "Hello World" program required several lines of ceremony code:

csharp
using System;

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

The Same Program with Top-level Statements

With top-level statements, the same program can be written in just one line:

csharp
Console.WriteLine("Hello World!");

That's it! No explicit namespace, no class declaration, and no Main method. All the ceremony code is implied by the compiler.

How Top-level Statements Work

When you use top-level statements, the C# compiler automatically:

  1. Creates an entry point method (similar to the traditional Main method)
  2. Places your top-level code inside this method
  3. Wraps everything in a hidden class inside a default namespace

Let's explore the key aspects of top-level statements:

Rules and Constraints

Top-level statements come with a few important rules:

  1. Single File: You can only have top-level statements in one file per project
  2. Positioning: Top-level statements must come after all using directives but before any namespace or type declarations
  3. Implicit Main: The top-level statements become the body of the application's entry point method

Working with Command-line Arguments

You might be wondering how to access command-line arguments without a Main(string[] args) method. With top-level statements, you can simply use the args variable, which is implicitly available:

csharp
// This program prints all command-line arguments
Console.WriteLine("Command line arguments:");

for (int i = 0; i < args.Length; i++)
{
Console.WriteLine($"Argument {i}: {args[i]}");
}

// Sample output when run with: dotnet run first second
// Command line arguments:
// Argument 0: first
// Argument 1: second

Return Values

If you need your program to return an exit code (as Main can return an int), you can simply use a return statement at the top level:

csharp
Console.WriteLine("Program running...");

// Some condition that determines success or failure
bool success = PerformOperation();

if (success)
{
Console.WriteLine("Operation successful");
return 0; // Success exit code
}
else
{
Console.WriteLine("Operation failed");
return 1; // Error exit code
}

bool PerformOperation()
{
// Some operation logic
return true; // or false based on operation result
}

Declaring Functions and Types

You can declare methods, classes, and other types after your top-level statements:

csharp
// Top-level statements
Console.WriteLine("Starting application...");
int result = Add(5, 3);
Console.WriteLine($"The result is {result}");
PrintMessage("Application complete!");

// Function declarations
int Add(int a, int b)
{
return a + b;
}

void PrintMessage(string message)
{
Console.WriteLine("**********");
Console.WriteLine(message);
Console.WriteLine("**********");
}

// Output:
// Starting application...
// The result is 8
// **********
// Application complete!
// **********

Note that these functions are local to the file and not accessible from other files unless explicitly made public and part of a namespace.

Using Asynchronous Code

Top-level statements support the await keyword directly, making asynchronous programming more straightforward:

csharp
using System;
using System.Net.Http;
using System.Threading.Tasks;

Console.WriteLine("Fetching data from the internet...");

string content = await DownloadContentAsync("https://api.github.com");
Console.WriteLine($"Downloaded {content.Length} characters");

async Task<string> DownloadContentAsync(string url)
{
using HttpClient client = new HttpClient();
return await client.GetStringAsync(url);
}

// Output:
// Fetching data from the internet...
// Downloaded XXXXX characters

Practical Applications

Top-level statements are particularly useful in several scenarios:

1. Learning and Teaching C#

For beginners, top-level statements remove distractions and let them focus on the actual code logic:

csharp
// A simple program to calculate area of a circle
Console.Write("Enter the radius of the circle: ");
if (double.TryParse(Console.ReadLine(), out double radius))
{
double area = Math.PI * radius * radius;
Console.WriteLine($"The area of the circle is {area:F2} square units.");
}
else
{
Console.WriteLine("Invalid input. Please enter a number.");
}

2. Small Utility Programs and Scripts

When writing small tools or scripts, top-level statements help you focus on the task at hand:

csharp
// Simple file processing utility
using System.IO;

string sourceDirectory = args.Length > 0 ? args[0] : ".";
string filePattern = args.Length > 1 ? args[1] : "*.txt";

var files = Directory.GetFiles(sourceDirectory, filePattern);
Console.WriteLine($"Found {files.Length} files matching pattern '{filePattern}' in '{sourceDirectory}'");

foreach (var file in files)
{
FileInfo info = new FileInfo(file);
Console.WriteLine($"{info.Name}: {info.Length} bytes, Last modified: {info.LastWriteTime}");
}

3. Quick Prototyping

For rapidly testing ideas or building prototypes:

csharp
using System.Text.Json;

// Quick API response prototype
var weatherData = new
{
Location = "New York",
Temperature = 72.5,
Conditions = "Partly Cloudy",
Forecast = new[] {
new { Day = "Monday", High = 75, Low = 65 },
new { Day = "Tuesday", High = 80, Low = 68 }
}
};

string json = JsonSerializer.Serialize(weatherData, new JsonSerializerOptions { WriteIndented = true });
Console.WriteLine(json);

// Output:
// {
// "Location": "New York",
// "Temperature": 72.5,
// "Conditions": "Partly Cloudy",
// "Forecast": [
// {
// "Day": "Monday",
// "High": 75,
// "Low": 65
// },
// {
// "Day": "Tuesday",
// "High": 80,
// "Low": 68
// }
// ]
// }

Combining with Namespaces and Classes

You can still define namespaces and classes in a file with top-level statements, but they must come after the top-level statements:

csharp
// Top-level statements
Console.WriteLine("Application starting...");
var calculator = new Calculator();
Console.WriteLine($"5 + 3 = {calculator.Add(5, 3)}");

// Class definition
public class Calculator
{
public int Add(int a, int b) => a + b;
public int Subtract(int a, int b) => a - b;
public int Multiply(int a, int b) => a * b;
public double Divide(int a, int b) => (double)a / b;
}

// Output:
// Application starting...
// 5 + 3 = 8

Best Practices

When working with top-level statements, consider these recommendations:

  1. Use for simplicity: Ideal for small programs, scripts, or learning exercises
  2. Avoid for complex applications: For larger, multi-file applications, stick with traditional structure
  3. Keep organized: Even though you can skip the ceremony, maintain good code organization
  4. Add namespaces: For libraries or components meant to be reused, define proper namespaces

Summary

Top-level statements in C# offer a more concise way to write programs by eliminating boilerplate code. This feature:

  • Removes the need for explicit namespace, class, and Main method declarations
  • Makes C# more approachable for beginners
  • Streamlines simple program development and prototyping
  • Supports command-line arguments, return values, and async/await
  • Works well alongside traditional C# code organization for larger projects

As a C# developer, top-level statements give you another tool to choose the right level of ceremony depending on your project's complexity. For quick scripts and simple programs, you can get straight to the point. For larger applications, you can still use the traditional structured approach where it makes sense.

Exercises

  1. Convert a traditional "Hello World" program to use top-level statements.
  2. Write a simple calculator program using top-level statements that takes two numbers and an operation as command-line arguments.
  3. Create a file processing utility that counts words in text files using top-level statements.
  4. Write an asynchronous program using top-level statements that downloads content from multiple web pages concurrently.

Additional Resources



If you spot any mistakes on this website, please let me know at feedback@compilenrun.com. I’d greatly appreciate your feedback! :)