Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
82 changes: 41 additions & 41 deletions docs/csharp/fundamentals/program-structure/main-command-line.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: "Main() and command-line arguments"
description: Learn about Main() and command-line arguments. The 'Main' method is the entry point of an executable program.
ms.date: 06/23/2025
ms.date: 12/15/2025
f1_keywords:
- "main_CSharpKeyword"
- "Main"
Expand All @@ -14,27 +14,27 @@ helpviewer_keywords:
---
# Main() and command-line arguments

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.
The runtime calls the `Main` method when you start a C# application. The `Main` method is the entry point of a C# application.

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#startupobject). The following example displays the number of command line arguments as its first action:
A C# program can have only one entry point. If you have more than one class with a `Main` method, you must use the **StartupObject** compiler option when you compile your program to specify which `Main` method serves as the entry point. For more information, see [**StartupObject** (C# Compiler Options)](../../language-reference/compiler-options/advanced.md#startupobject). The following example displays the number of command line arguments as its first action:

:::code language="csharp" source="snippets/main-command-line/TestClass.cs":::

You can also use top-level statements in one file as the entry point for your application. Just as the `Main` method, top-level statements can also [return values](#main-return-values) and access [command-line arguments](#command-line-arguments). For more information, see [Top-level statements](top-level-statements.md). The following example uses a `foreach` loop to display the command-line arguments using the `args` variable, and at the end of the program returns a success code (`0`):
You can also use top-level statements in one file as the entry point for your application. Like the `Main` method, top-level statements can [return values](#main-return-values) and access [command-line arguments](#command-line-arguments). For more information, see [Top-level statements](top-level-statements.md). The following example uses a `foreach` loop to display the command-line arguments by using the `args` variable, and at the end of the program returns a success code (`0`):

:::code language="csharp" source="snippets/top-level-statements-1/Program.cs":::

Beginning with C# 14, programs can be [*file-based apps*](./index.md#building-and-running-c-programs), where a single file contains the program. You run *file-based apps* with the command `dotnet run <file.cs>`, or using the `#!/usr/local/share/dotnet/dotnet run` directive as the first line (unix shells only).
Beginning with C# 14, programs can be [*file-based apps*](./index.md#building-and-running-c-programs), where a single file contains the program. You run *file-based apps* by using the command `dotnet <file.cs>`, or by using the `#!/usr/bin/env dotnet run` directive as the first line (Unix shells only).

## Overview

- The `Main` method is the entry point of an executable program; it's where the program control starts and ends.
- `Main` must be declared inside a class or struct. The enclosing `class` can be `static`.
- The `Main` method is the entry point of an executable program. It's where the program control starts and ends.
- You must declare `Main` inside a class or struct. The enclosing `class` can be `static`.
- `Main` must be [`static`](../../language-reference/keywords/static.md).
- `Main` can have any [access modifier](../../programming-guide/classes-and-structs/access-modifiers.md) (except `file`).
- `Main` can either have a `void`, `int`, `Task`, or `Task<int>` return type.
- `Main` can have any [access modifier](../../programming-guide/classes-and-structs/access-modifiers.md).
- `Main` can return `void`, `int`, `Task`, or `Task<int>`.
- If and only if `Main` returns a `Task` or `Task<int>`, the declaration of `Main` can include the [`async`](../../language-reference/keywords/async.md) modifier. This rule 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 isn't treated as the first command-line argument in the `args` array, but it's the first element of the <xref:System.Environment.GetCommandLineArgs> method.
- You can declare the `Main` method 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 zero-indexed command-line arguments. Unlike C and C++, the name of the program isn't treated as the first command-line argument in the `args` array, but it's the first element of the <xref:System.Environment.GetCommandLineArgs> method.

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

Expand All @@ -49,10 +49,10 @@ static async Task Main(string[] args) { }
static async Task<int> Main(string[] args) { }
```

The preceding examples don't specify an access modifier, so they're implicitly `private` by default. It's possible to specify any explicit access modifier.
The preceding examples don't specify an access modifier, so they're implicitly `private` by default. You can specify any explicit access modifier.

> [!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`.
> By using `async` and `Task` or `Task<int>` return types, you simplify program code when console applications need to start and `await` asynchronous operations in `Main`.

## Main() return values

Expand Down Expand Up @@ -86,13 +86,13 @@ Create a new application by running `dotnet new console`. Modify the `Main` meth

Remember to save this program as *MainReturnValTest.cs*.

When a program is executed in Windows, any value returned from the `Main` function is stored in an environment variable. This environment variable can be retrieved using `ERRORLEVEL` from a batch file, or `$LastExitCode` from PowerShell.
When you execute a program in Windows, the system stores any value returned from the `Main` function in an environment variable. You can retrieve this environment variable by using `ERRORLEVEL` from a batch file, or `$LastExitCode` from PowerShell.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure the system doesn't store the return value in an environment variable (of which process would that be?). It's the shell which retrieves the exit code, and stores it in an internal variable (neither ERRORLEVEL nor LastExitCode are environment variables).


You can build the application using the [dotnet CLI](../../../core/tools/dotnet.md) `dotnet build` command.
You can build the application by using the [dotnet CLI](../../../core/tools/dotnet.md) `dotnet build` command.

Next, create a PowerShell script to run the application and display the result. Paste the following code into a text file and save it as `test.ps1` in the folder that contains the project. Run the PowerShell script by typing `test.ps1` at the PowerShell prompt.

Because the code returns zero, the batch file reports success. However, if you change MainReturnValTest.cs to return a non-zero value and then recompile the program, subsequent execution of the PowerShell script reports failure.
Because the code returns zero, the batch file reports success. However, if you change MainReturnValTest.cs to return a nonzero value and then recompile the program, subsequent execution of the PowerShell script reports failure.

```powershell
dotnet run
Expand All @@ -116,21 +116,21 @@ When you declare an `async` return value for `Main`, the compiler generates the

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

In both examples main body of the program is within the body of `AsyncConsoleWork()` method.
In both examples, the main body of the program is within the body of the `AsyncConsoleWork()` method.

An advantage of declaring `Main` as `async` is that the compiler always generates the correct code.

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 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();`
- `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 the examples used `async` modifier on the `Main` method, the compiler would generate the same code.
> If the examples use the `async` modifier on the `Main` method, the compiler generates the same code.

## Command-Line Arguments
## Command-line arguments

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

Expand All @@ -141,7 +141,7 @@ You can send arguments to the `Main` method by defining the method in one of the
| `static async Task Main(string[] args)` | Uses `await` but doesn't return a value |
| `static async Task<int> Main(string[] args)` | Return a value and uses `await` |

If the arguments aren't used, you can omit `args` from the method declaration for slightly simpler code:
If you don't use the arguments, you can omit `args` from the method declaration for slightly simpler code:

| `Main` declaration | `Main` method code |
|---------------------------------|-----------------------------------------|
Expand All @@ -153,7 +153,7 @@ If the arguments aren't used, you can omit `args` from the method declaration fo
> [!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 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:
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:

:::code language="csharp" source="snippets/main-command-line/Program.cs" ID="Snippet4":::

Expand Down Expand Up @@ -189,28 +189,28 @@ To compile and run the application from a command prompt, follow these steps:

1. Paste the following code into any text editor, and then save the file as a text file with the name *Factorial.cs*.

:::code language="csharp" source="./snippets/main-command-line/Factorial.cs":::
:::code language="csharp" source="./snippets/main-command-line/Factorial.cs":::

At the beginning of the `Main` method the program tests if input arguments weren't supplied comparing length of `args` argument to `0` and displays the help if no arguments are found.<br/>
If arguments are provided (`args.Length` is greater than 0), the program tries to convert the input arguments to numbers. This example throws an exception if the argument isn't a number.<br/>
After factorial is calculated (stored in `result` variable of type `long`), the verbose result is printed depending on the `result` variable.
At the beginning of the `Main` method, the program tests if input arguments weren't supplied by comparing the length of the `args` argument to `0` and displays the help if no arguments are found.<br/>
If arguments are provided (`args.Length` is greater than 0), the program tries to convert the input arguments to numbers. This example throws an exception if the argument isn't a number.<br/>
After factorial is calculated (stored in `result` variable of type `long`), the verbose result is printed depending on the `result` variable.

2. From the **Start** screen or **Start** menu, open a Visual Studio **Developer Command Prompt** window, and then navigate to the folder that contains the file that you created.
1. From the **Start** screen or **Start** menu, open a Visual Studio **Developer Command Prompt** window, and then navigate to the folder that contains the file that you created.

3. To compile the application, enter the following command:
`dotnet build`
If your application has no compilation errors, a binary file named *Factorial.dll* is created.
4. Enter the following command to calculate the factorial of 3:
`dotnet run -- 3`
5. If 3 is entered on command line as the program's argument, the output reads: `The factorial of 3 is 6.`
1. To compile the application, enter the following command:

`dotnet build`

If your application has no compilation errors, the build process creates a binary file named *Factorial.dll*.

1. Enter the following command to calculate the factorial of 3:

`dotnet run -- 3`

1. If you enter 3 on the command line as the program's argument, the output reads: `The factorial of 3 is 6.`

> [!NOTE]
> When running an application in Visual Studio, you can specify command-line arguments in the [Debug Page, Project Designer](/visualstudio/ide/reference/debug-page-project-designer).
> When running an application in Visual Studio, specify command-line arguments in the [Debug Page, Project Designer](/visualstudio/ide/reference/debug-page-project-designer).

## C# language specification

Expand Down
14 changes: 7 additions & 7 deletions docs/csharp/language-reference/keywords/access-modifiers.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,29 @@
---
description: "Access Modifiers - C# Reference"
title: "Access Modifiers"
ms.date: 09/15/2022
ms.date: 12/15/2025
helpviewer_keywords:
- "access modifiers [C#]"
---
# Access Modifiers (C# Reference)
# Access modifiers (C# reference)

Access modifiers are keywords used to specify the declared accessibility of a member or a type. This section introduces the five access modifiers:
Use access modifiers to specify the declared accessibility of a member or a type. This section introduces the four access modifiers:

- `public`
- `protected`
- `internal`
- `private`
- `file`

The following seven accessibility levels can be specified using the access modifiers:
By using these access modifiers, you can specify the following six accessibility levels:

- [`public`](public.md): Access isn't restricted.
- [`public`](public.md): No access restrictions.
- [`protected`](protected.md): Access is limited to the containing class or types derived from the containing class.
- [`internal`](internal.md): Access is limited to the current assembly.
- [`protected internal`](protected-internal.md): Access is limited to the current assembly or types derived from the containing class.
- [`private`](private.md): Access is limited to the containing type.
- [`private protected`](private-protected.md): Access is limited to the containing class or types derived from the containing class within the current assembly.
- [`file`](file.md): The declared type is only visible in the current source file. File scoped types are generally used for source generators.

In addition, a top-level (non-nested) type can use the [`file`](file.md) modifier. The declared type is only visible in the current source file. File scoped types are generally used for source generators. You can't combine the `file` modifier with any access modifier.

This section also introduces the following concepts:

Expand Down
Loading