Top-level Statements in C#



Top-level statements are a feature in C# that allow us to write code directly in a file without wrapping it inside a class or Main method. This feature was introduced in C# 9.0 to make programs easier to start.

When we use top-level statements, our code starts executing from the first line without any extra structure. This approach removes unnecessary complexity and helps us focus on what our code needs to do rather than how it is organized.

How C# Worked Before Top-level Statements

Before top-level statements, every C# program had to start with a Main method, which acted as the entry point for the program. The Main method was always placed inside a class, usually named Program. This was the standard structure for any C# application.

Example

Here's an example of how the traditional approach looked. Even for printing "Hello, World!", we had to write multiple lines of code. While this works well for large applications, it feels unnecessarily long for small scripts that only need a few lines.

using System;

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

Following is the output after running the above program.

Hello, World!

Using Top-Level Statements

When we use top-level statements, we can skip writing the class and the Main method completely. The compiler automatically creates them for us in the background, so we only need to focus on the logic that matters.

Example

Here's an example of how the same program looks with top-level statements.

using System;

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

When we run it, the compiler automatically handles the structure that was previously required and gives the same output as before.

Hello, World!

How the Compiler Handles It

When we use top-level statements, the compiler automatically creates a hidden class and a Main method for us. It places our code inside that method, so everything still runs the same way as in a regular C# program.

For example, when we write the following code -

Console.WriteLine("Hello");

The compiler converts it internally into the following code -

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

So, in this way, top-level statements allow us to write less code while keeping the program's behavior exactly the same.

Rules for Using Top-Level Statements

Following are the important rules we need to follow for using top-level statements -

  • One file only: Only one file in your project can contain top-level statements. If more than one file contains them, the compiler will not know which one to use as the entry point, and it will produce an error.
  • Execution order: Our code runs in the order we write it, starting from the first line of top-level code and continues down until the end of the file.
  • Using directives and global attributes: These must come before any top-level statements. We cannot place a using statement after a line of code
  • Declaring other elements: We can declare functions, classes, records, and structs below top-level statements, but not above them.
  • Async code: If we use await, the compiler automatically makes the hidden Main method asynchronous. We don't need to declare it ourselves.
  • Command-line arguments: These are automatically available through the variable args, even though we do not see it declared.

Example

Here is an example that shows how top-level statements work. The program starts executing from the first line. It prints a welcome message, calculates the sum of two numbers, and shows the result all without a class or Main method.

using System;

Console.WriteLine("Welcome to tutorialspoint C# top-level statements!");
int a = 5;
int b = 10;
Console.WriteLine("The sum of " + a + " and " + b + " is " + (a + b));

Following is the output of the above program.

Welcome to C# top-level statements!
The sum of 5 and 10 is 15

Declaring and Using Functions

We can declare and use functions below top-level statements. These functions become part of the automatically created class, and we can call them from anywhere in our top-level code.

Example

In this example, the GreetUser function is declared after the top-level statements but is called above. The compiler recognizes it as a local function and runs it properly.

using System;

Console.WriteLine("Program started");
GreetUser("Valentine");

void GreetUser(string name)
{
    Console.WriteLine("Hello, " + name + ". Nice to meet you!");
}

Following is the output of the above program -

Program started
Hello, Alice. Nice to meet you!

Using Asynchronous Code

Top-level statements also support asynchronous programming. We can use the await keyword directly without declaring an async Main method.

Example

In this example, we use HttpClient to fetch data from a website using the await keyword. The compiler automatically makes the hidden Main method asynchronous, so we don't need to write it manually.

using System;
using System.Net.Http;

HttpClient client = new HttpClient();
string data = await client.GetStringAsync("https://example.com");
Console.WriteLine(data);

Below is the output of the above program that shows the downloaded content from the given URL.

<!doctype html><html lang="en"><head><title>Example Domain</title><
meta name="viewport" content="width=device-width, initial-scale=1"><
style>body{background:#eee;width:60vw;margin:15vh auto;font-family:system-ui,sans-serif}
h1{font-size:1.5em}div{opacity:0.8}a:link,a:visited{color:#348}</style><body><div><
h1>Example Domain</h1><p>This domain is for use in documentation examples without needing permission.
Avoid use in operations.<p><a href="https://iana.org/domains/example">Learn more</a></div></body></html>

Working with Namespaces and Custom Types

We can use namespaces and create custom types while keeping top-level statements simple. To work correctly in a single file, the top-level code must come first, and custom types must be declared afterward or placed in separate files.

Example

In this example, the top-level code runs first, and then it uses the Helper class from the Utilities namespace.

using System;

// Top-level code that runs immediately
Console.WriteLine("Program running");

// Call a method from the Helper class
Helper.DisplayMessage();

// Helper class declared below top-level statements
public static class Helper
{
    public static void DisplayMessage()
    {
        Console.WriteLine("Message from the Helper class");
    }
}

Following is the output of the above program -

Program running
Message from the Helper class

Accessing Command-Line Arguments

We can access command-line arguments in top-level statements using the automatically available args variable. It works the same way as in traditional C# programs.

Example

Here's an example where we read command-line arguments directly using top-level statements. The program checks if any arguments are passed. If yes, it shows their count and prints each one. Otherwise, it displays a message saying no arguments were given.

using System;

if (args.Length > 0)
{
    Console.WriteLine($"You provided {args.Length} argument(s):");
    foreach (string argument in args)
    {
        Console.WriteLine(argument);
    }
}
else
{
    Console.WriteLine("No arguments were provided.");
}

Below we can see the output after running the program.

No arguments were provided.

Limitations

Following are some limitations we need to know when using top-level statements

  • Only one file in a project can have top-level statements.
  • We cannot create another Main method in the same project.
  • Top-level statements must always come before any type declarations.
  • They are not suitable for large or complex applications that depend on advanced setups like dependency injection or configuration systems.

Example Using Top-Level Statements

Here's an example where we create a calculator using top-level statements. The program starts running directly from the first line and performs all calculations without any class or Main method declaration.

using System;

Console.WriteLine("Calculator\n");

double number1 = 12;
double number2 = 4;

Console.WriteLine($"Addition: {number1 + number2}");
Console.WriteLine($"Subtraction: {number1 - number2}");
Console.WriteLine($"Multiplication: {number1 * number2}");
Console.WriteLine($"Division: {number1 / number2}");

Following is the output of the above program -

Calculator

Addition: 16
Subtraction: 8
Multiplication: 48
Division: 3

Conclusion

In this chapter, we learned about top-level statements in C#. These statements are written at the top of the file to simplify code by removing the need for a Main method or class. The program starts executing directly from the first line. It is useful in small projects but not suitable for large or complex applications.

Advertisements