Skip to content

Fix misleading in the Main doc about async/Task #41356

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 26 commits into from
Jun 21, 2024
Merged
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 67 additions & 48 deletions docs/csharp/fundamentals/program-structure/main-command-line.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ helpviewer_keywords:
---
# Main() and command-line arguments

The `Main` method is the entry point of a C# application. (Libraries and services do not require a `Main` method as an entry point.) When the application is started, the `Main` method is the first method that is invoked.
The `Main` method is the entry point of a C# application. When the application is started, the `Main` method is the first method that is invoked.

There can only be one entry point in a C# program. If you have more than one class that has a `Main` method, you must compile your program with the **StartupObject** compiler option to specify which `Main` method to use as the entry point. For more information, see [**StartupObject** (C# Compiler Options)](../../language-reference/compiler-options/advanced.md#mainentrypoint-or-startupobject).

Expand All @@ -29,47 +29,50 @@ For more information, see [Top-level statements](top-level-statements.md).
## Overview

- The `Main` method is the entry point of an executable program; it is where the program control starts and ends.
- `Main` is declared inside a class or struct. `Main` must be [`static`](../../language-reference/keywords/static.md) and it need not be [`public`](../../language-reference/keywords/public.md). (In the earlier example, it receives the default access of [`private`](../../language-reference/keywords/private.md).) An enclosing `class` can be `static`.
- `Main` must be declared inside a class or struct. The enclosing `class` can be `static`.
- `Main` must be [`static`](../../language-reference/keywords/static.md).
- `Main` can have a [`public`](../../language-reference/keywords/public.md), [`protected`](../../language-reference/keywords/protected.md), [`private`](../../language-reference/keywords/private.md), [`protected internal`](../../language-reference/keywords/protected-internal.md) and [`private protected`](../../language-reference/keywords/private-protected.md) access modifier. If not specified, it receives the default access of private.
- `Main` can either have a `void`, `int`, `Task`, or `Task<int>` return type.
- If and only if `Main` returns a `Task` or `Task<int>`, the declaration of `Main` may include the [`async`](../../language-reference/keywords/async.md) modifier. This specifically excludes an `async void Main` method.
- The `Main` method can be declared with or without a `string[]` parameter that contains command-line arguments. When using Visual Studio to create Windows applications, you can add the parameter manually or else use the <xref:System.Environment.GetCommandLineArgs> method to obtain the command-line arguments. Parameters are read as zero-indexed command-line arguments. Unlike C and C++, the name of the program is not treated as the first command-line argument in the `args` array, but it is the first element of the <xref:System.Environment.GetCommandLineArgs> method.

The following list shows valid `Main` signatures:
The following list shows the most common `Main` declarations:

```csharp
public static void Main() { }
public static int Main() { }
public static void Main(string[] args) { }
public static int Main(string[] args) { }
public static async Task Main() { }
public static async Task<int> Main() { }
public static async Task Main(string[] args) { }
public static async Task<int> Main(string[] args) { }
static void Main() { }
static int Main() { }
static void Main(string[] args) { }
static int Main(string[] args) { }
static async Task Main() { }
static async Task<int> Main() { }
static async Task Main(string[] args) { }
static async Task<int> Main(string[] args) { }
```

The preceding examples all use the `public` accessor modifier. That's typical, but not required.
The preceding examples don't specify an access modifier, so it receives the default access of private. That's typical, but it possible to specify a other access modifier.

The addition of `async` and `Task`, `Task<int>` return types simplifies program code when console applications need to start and `await` asynchronous operations in `Main`.
> [!TIP]
> The addition of `async` and `Task`, `Task<int>` return types simplifies program code when console applications need to start and `await` asynchronous operations in `Main`.

## Main() return values

You can return an `int` from the `Main` method by defining the method in one of the following ways:

| `Main` method code | `Main` signature |
|--------------------------------|----------------------------------------------|
| No use of `args` or `await` | `static int Main()` |
| Uses `args`, no use of `await` | `static int Main(string[] args)` |
| No use of `args`, uses `await` | `static async Task<int> Main()` |
| Uses `args` and `await` | `static async Task<int> Main(string[] args)` |
| `Main` declaration | `Main` method code |
|----------------------------------------------|--------------------------------|
| `static int Main()` | No use of `args` or `await` |
| `static int Main(string[] args)` | Uses `args`, no use of `await` |
| `static async Task<int> Main()` | No use of `args`, uses `await` |
| `static async Task<int> Main(string[] args)` | Uses `args` and `await` |

If the return value from `Main` is not used, returning `void` or `Task` allows for slightly simpler code.

| `Main` method code | `Main` signature |
|--------------------------------|-----------------------------------------|
| No use of `args` or `await` | `static void Main()` |
| Uses `args`, no use of `await` | `static void Main(string[] args)` |
| No use of `args`, uses `await` | `static async Task Main()` |
| Uses `args` and `await` | `static async Task Main(string[] args)` |
| `Main` declaration | `Main` method code |
|-----------------------------------------|--------------------------------|
| `static void Main()` | No use of `args` or `await` |
| `static void Main(string[] args)` | Uses `args`, no use of `await` |
| `static async Task Main()` | No use of `args`, uses `await` |
| `static async Task Main(string[] args)` | Uses `args` and `await` |

However, returning `int` or `Task<int>` enables the program to communicate status information to other programs or scripts that invoke the executable file.

Expand Down Expand Up @@ -105,64 +108,80 @@ Execution succeeded
Return value = 0
```

### Async Main return values
### Main return Task

When the application entry point returns a `Task` or `Task<int>`, the compiler generates a new entry point that calls the entry point method declared in the application code. Assuming that this entry point is called `$GeneratedMain`, the compiler generates the following code for these entry points:

When you declare an `async` return value for `Main`, the compiler generates the boilerplate code for calling asynchronous methods in `Main`. If you don't specify the `async` keyword, you need to write that code yourself, as shown in the following example. The code in the example ensures that your program runs until the asynchronous operation is completed:
- `static Task Main()` results in the compiler emitting the equivalent of `private static void $GeneratedMain() => Main().GetAwaiter().GetResult();`
- `static Task Main(string[])` results in the compiler emitting the equivalent of `private static void $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();`
- `static Task<int> Main()` results in the compiler emitting the equivalent of `private static int $GeneratedMain() => Main().GetAwaiter().GetResult();`
- `static Task<int> Main(string[])` results in the compiler emitting the equivalent of `private static int $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();`

> [!NOTE]
>If `Main` include the `async` modifier, the compiler would generate the same code.

For this example:
```csharp
class AsyncMainReturnValTest
class Program
{
public static void Main()
static async Task Main(string[] args)
{
AsyncConsoleWork().GetAwaiter().GetResult();
await WorkAsync();
}

private static async Task<int> AsyncConsoleWork()
private static async Task WorkAsync()
{
// Main body here
return 0;
// Some work here
}
}
```

This boilerplate code can be replaced by:

:::code language="csharp" source="snippets/main-arguments/Program.cs" id="AsyncMain":::

An advantage of declaring `Main` as `async` is that the compiler always generates the correct code.
The compilater will generate the boilerplate code :
```csharp
class Program
{
public static void $GeneratedMain()
{
WorkAsync().GetAwaiter().GetResult();
}

When the application entry point returns a `Task` or `Task<int>`, the compiler generates a new entry point that calls the entry point method declared in the application code. Assuming that this entry point is called `$GeneratedMain`, the compiler generates the following code for these entry points:
static async Task Main(string[] args)
{
await WorkAsync();
}

- `static Task Main()` results in the compiler emitting the equivalent of `private static void $GeneratedMain() => Main().GetAwaiter().GetResult();`
- `static Task Main(string[])` results in the compiler emitting the equivalent of `private static void $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();`
- `static Task<int> Main()` results in the compiler emitting the equivalent of `private static int $GeneratedMain() => Main().GetAwaiter().GetResult();`
- `static Task<int> Main(string[])` results in the compiler emitting the equivalent of `private static int $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult();`
private static async Task WorkAsync()
{
// Some work here
}
}
```

> [!NOTE]
>If the examples used `async` modifier on the `Main` method, the compiler would generate the same code.
> In fact, the generator will not generate the method with the name `$GeneratedMain`, but with a specific IL name.

## Command-Line Arguments

You can send arguments to the `Main` method by defining the method in one of the following ways:

| `Main` method code | `Main` signature |
| `Main` method code | `Main` declaration |
|------------------------------------|----------------------------------------------|
| No return value, no use of `await` | `static void Main(string[] args)` |
| Return value, no use of `await` | `static int Main(string[] args)` |
| No return value, uses `await` | `static async Task Main(string[] args)` |
| Return value, uses `await` | `static async Task<int> Main(string[] args)` |

If the arguments are not used, you can omit `args` from the method signature for slightly simpler code:
If the arguments are not used, you can omit `args` from the method declaration for slightly simpler code:

| `Main` method code | `Main` signature |
| `Main` method code | `Main` declaration |
|------------------------------------|---------------------------------|
| No return value, no use of `await` | `static void Main()` |
| Return value, no use of `await` | `static int Main()` |
| No return value, uses `await` | `static async Task Main()` |
| Return value, uses `await` | `static async Task<int> Main()` |

> [!NOTE]
> You can also use <xref:System.Environment.CommandLine%2A?displayProperty=nameWithType> or <xref:System.Environment.GetCommandLineArgs%2A?displayProperty=nameWithType> to access the command-line arguments from any point in a console or Windows Forms application. To enable command-line arguments in the `Main` method signature in a Windows Forms application, you must manually modify the signature of `Main`. The code generated by the Windows Forms designer creates `Main` without an input parameter.
> You can also use <xref:System.Environment.CommandLine%2A?displayProperty=nameWithType> or <xref:System.Environment.GetCommandLineArgs%2A?displayProperty=nameWithType> to access the command-line arguments from any point in a console or Windows Forms application. To enable command-line arguments in the `Main` method declaration in a Windows Forms application, you must manually modify the declaration of `Main`. The code generated by the Windows Forms designer creates `Main` without an input parameter.

The parameter of the `Main` method is a <xref:System.String> array that represents the command-line arguments. Usually you determine whether arguments exist by testing the `Length` property, for example:

Expand Down