From 56151df9c3001556a5dc14ac75afefcd1499cb33 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 19 Jun 2025 16:14:01 -0400 Subject: [PATCH 1/6] publish spec and add preprocessor reference First commit: Publish the C# speclet for ignored preprocessor directives, and make the updates for the ignored directives used for file based programs. --- docfx.json | 7 ++- .../preprocessor-directives.md | 63 ++++++++++++++++++- docs/csharp/specification/toc.yml | 2 + 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/docfx.json b/docfx.json index 01e64d4904d34..06dd832df41a5 100644 --- a/docfx.json +++ b/docfx.json @@ -61,7 +61,8 @@ "partial-events-and-constructors.md", "null-conditional-assignment.md", "extensions.md", - "user-defined-compound-assignment.md" + "user-defined-compound-assignment.md", + "ignored-directives.md" ], "src": "_csharplang/proposals", "dest": "csharp/language-reference/proposals", @@ -533,7 +534,7 @@ "_csharplang/proposals/csharp-11.0/*.md": "09/30/2022", "_csharplang/proposals/csharp-12.0/*.md": "08/15/2023", "_csharplang/proposals/csharp-13.0/*.md": "10/31/2024", - "_csharplang/proposals/*.md": "04/08/2025", + "_csharplang/proposals/*.md": "06/19/2025", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "11/08/2022", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "11/08/2023", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "11/09/2024", @@ -713,6 +714,7 @@ "_csharplang/proposals/null-conditional-assignment.md": "Null conditional assignment", "_csharplang/proposals/extensions.md": "Extension members", "_csharplang/proposals/user-defined-compound-assignment.md": "User-defined compound assignment", + "_csharplang/proposals/ignored-directives.md": "Ignored preprocessor directives", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "C# compiler breaking changes since C# 10", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "C# compiler breaking changes since C# 11", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "C# compiler breaking changes since C# 12", @@ -838,6 +840,7 @@ "_csharplang/proposals/null-conditional-assignment.md": "This proposal allows the null conditional operator to be used for the destination of assignment expressions. This allows you to assign a value to a property or field only if the left side is not null.", "_csharplang/proposals/extensions.md": "This proposal enables new kinds of extension members. These new extension members support extension properties, extension static members, including extension operators.", "_csharplang/proposals/user-defined-compound-assignment.md": "This proposal introduces user-defined compound assignment operators. Developers can override compound assignment, increment, and decrement operators.", + "_csharplang/proposals/ignored-directives.md": "This proposal allows a source file to include ignored directives. In most cases, ignored directives are used for file based programs, for example `#!`", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 7.md": "Learn about any breaking changes since the initial release of C# 10 and included in C# 11", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 8.md": "Learn about any breaking changes since the initial release of C# 11 and included in C# 12", "_roslyn/docs/compilers/CSharp/Compiler Breaking Changes - DotNet 9.md": "Learn about any breaking changes since the initial release of C# 12 and included in C# 13", diff --git a/docs/csharp/language-reference/preprocessor-directives.md b/docs/csharp/language-reference/preprocessor-directives.md index c136e05808a10..ff16d093b06e0 100644 --- a/docs/csharp/language-reference/preprocessor-directives.md +++ b/docs/csharp/language-reference/preprocessor-directives.md @@ -1,7 +1,7 @@ --- description: "Learn the different C# preprocessor directives that control conditional compilation, warnings, nullable analysis, and more" title: "Preprocessor directives" -ms.date: 01/14/2025 +ms.date: 06/19/2025 f1_keywords: - "cs.preprocessor" - "#nullable" @@ -20,6 +20,10 @@ f1_keywords: - "#pragma warning" - "#pragma checksum" - "defaultline_CSharpKeyword" + - "#!" + - "#:sdk" + - "#:property" + - "#:package" helpviewer_keywords: - "preprocessor directives [C#]" - "keywords [C#], preprocessor directives" @@ -43,6 +47,63 @@ helpviewer_keywords: Although the compiler doesn't have a separate preprocessor, the directives described in this section are processed as if there were one. You use them to help in conditional compilation. Unlike C and C++ directives, you can't use these directives to create macros. A preprocessor directive must be the only instruction on a line. +## File based programs + +*File based programs* are programs that are compiled and run using `dotnet run Program.cs` (or any `*.cs` file). The C# compiler ignores these preprocessor directives, but the build system parses them to produce the output. These directives generate warnings when encountered in a project-based compilation. + +The C# compiler ignores any preprocessor directive that starts with `#:` or `#!`. + +The `#!` preprocessor directive enables unix shells to directly execute a C# file using `dotnet run`. For example: + +```csharp +#!/usr/bin/dotnet run +Console.WriteLine("Hello"); +``` + +The preceding code snippet informs a unix shell to execute the file using `/usr/bin/dotnet run`. The `#!` line must be the first line in the file, and the following tokens are the program to run. You need to enable the *execute* (`x`) permission on the C# file for that feature. + +The `#:` directives that are used in file based programs include: + +- `#:sdk`: + + The first instance specifies the value for the `` node. Subsequent instances specify `` node. The version can be omitted. For example: + + ```csharp + #:sdk Microsoft.NET.Sdk.Web + ``` + +- `#:property`: + + Instances of `#:property` are translated into property elements in a ``. A token of the form `Name=value` must follow the `property` token. The following are valid `property` tokens: + + ```csharp + #:property TargetFramework=net11.0 + #:property LangVersion=preview + ``` + + The preceding two properties are translated into: + + ```xml + net11.0 + preview + ``` + +- `#:package`: + + Instances `#:package` are translated into `PackageReference` elements to include NuGet packages with the specified version to your file. For example: + + ```csharp + #:package System.CommandLine@2.0.0-* + ``` + + The preceding preprocessor token is translated into: + + ```xml + + ``` + +Tools can add new tokens following the `#:` convention. + ## Nullable context The `#nullable` preprocessor directive sets the *annotations* and *warning* flags in the *nullable context*. This directive controls whether nullable annotations have effect, and whether nullability warnings are given. Each flag is either *disabled* or *enabled*. diff --git a/docs/csharp/specification/toc.yml b/docs/csharp/specification/toc.yml index 7c9a0759f63a9..c843abdd0f15c 100644 --- a/docs/csharp/specification/toc.yml +++ b/docs/csharp/specification/toc.yml @@ -79,6 +79,8 @@ items: href: ../../../_csharplang/proposals/csharp-10.0/enhanced-line-directives.md - name: "Escape sequence '\\e'" href: ../../../_csharplang/proposals/csharp-13.0/esc-escape-sequence.md + - name: "Ignored directives" + href: ../../../_csharplang/proposals/ignored-directives.md - name: Basic concepts items: - name: Top-level statements From 237de6cc12f7fc15395589ce32f04f8f661e3796 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 20 Jun 2025 15:27:24 -0400 Subject: [PATCH 2/6] Add file-based programs to get started --- docs/csharp/tour-of-csharp/overview.md | 18 +++++++++++++++++- .../file-based-programs/hello-world.cs | 2 ++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 docs/csharp/tour-of-csharp/snippets/file-based-programs/hello-world.cs diff --git a/docs/csharp/tour-of-csharp/overview.md b/docs/csharp/tour-of-csharp/overview.md index 6bc393c9158fe..6f3b230ff958a 100644 --- a/docs/csharp/tour-of-csharp/overview.md +++ b/docs/csharp/tour-of-csharp/overview.md @@ -1,7 +1,7 @@ --- title: Overview description: New to C#? Learn the basics of the language. Start with this overview. -ms.date: 03/17/2025 +ms.date: 06/20/2025 --- # A tour of the C# language @@ -34,6 +34,22 @@ The `Program` class declared by the "Hello, World" program has a single member, > [!TIP] > The examples in this article give you a first look at C# code. Some samples might show elements of C# that you're not familiar with. When you're ready to learn C#, start with our [beginner tutorials](./tutorials/index.md), or dive into the links in each section. If you're experienced in [Java](./tips-for-java-developers.md), [JavaScript](./tips-for-javascript-developers.md), [TypeScript](./tips-for-javascript-developers.md), or [Python](./tips-for-python-developers.md), read our tips to help you find the information you need to quickly learn C#. +## File based programs + +C# is a *compiled* language. In most C# programs, you use the [`dotnet build`](../../core/tools/dotnet-build.md) command to compile a group of source files into a binary package. Then, you use the [`dotnet run`](../../core/tools/dotnet-run.md) command to run the program. (You can simplify this process because `dotnet run` compiles the program before running it if necessary.) These tools support a rich language of configuration options and command line switches. The `dotnet` command line interpreter (CLI), which is included in the .NET SDK, provides many [tools](../../core/tools/index.md) to generate and modify these files. + +Beginning with C# 14 and .NET 10, you can create *file based programs*, which simplifies building and running csharp programs. You use the `dotnet run` command to run a program contained in a single `*.cs` file. For example, you run the following snippet, when stored in the file `hello-world.cs`, by typing `dotnet run hello-world.cs`: + +:::code language="csharp" source="./snippets/file-based-programs/hello-world.cs"::: + +The first line of the program contains the `#!` sequence for unix shells. On any unix system, if you set the *execute* (`+x`) permission on a C# file, you can run the C# file from the command line: + +```bash +./hello-world.cs +``` + +The source for these programs must be a single file, but otherwise all C# syntax is valid. You can use file based programs for small command-line utilities, prototypes, or other experiments. + ## Familiar C# features C# is approachable for beginners yet offers advanced features for experienced developers writing specialized applications. You can be productive quickly. You can learn more specialized techniques as you need them for your applications. diff --git a/docs/csharp/tour-of-csharp/snippets/file-based-programs/hello-world.cs b/docs/csharp/tour-of-csharp/snippets/file-based-programs/hello-world.cs new file mode 100644 index 0000000000000..874f327348a0b --- /dev/null +++ b/docs/csharp/tour-of-csharp/snippets/file-based-programs/hello-world.cs @@ -0,0 +1,2 @@ +#!/usr/bin/dotnet run +Console.WriteLine("Hello, World!"); From 7cfce50ff758268e5bdd81d868ec54723e3331c0 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 20 Jun 2025 16:00:32 -0400 Subject: [PATCH 3/6] Fix host location And, set +x permissions on hello-world.cs --- docs/csharp/language-reference/preprocessor-directives.md | 4 ++-- docs/csharp/tour-of-csharp/overview.md | 2 +- .../snippets/file-based-programs/hello-world.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) mode change 100644 => 100755 docs/csharp/tour-of-csharp/snippets/file-based-programs/hello-world.cs diff --git a/docs/csharp/language-reference/preprocessor-directives.md b/docs/csharp/language-reference/preprocessor-directives.md index ff16d093b06e0..ddb78db427c15 100644 --- a/docs/csharp/language-reference/preprocessor-directives.md +++ b/docs/csharp/language-reference/preprocessor-directives.md @@ -56,11 +56,11 @@ The C# compiler ignores any preprocessor directive that starts with `#:` or `#!` The `#!` preprocessor directive enables unix shells to directly execute a C# file using `dotnet run`. For example: ```csharp -#!/usr/bin/dotnet run +#!/usr/local/share/dotnet/dotnet run Console.WriteLine("Hello"); ``` -The preceding code snippet informs a unix shell to execute the file using `/usr/bin/dotnet run`. The `#!` line must be the first line in the file, and the following tokens are the program to run. You need to enable the *execute* (`x`) permission on the C# file for that feature. +The preceding code snippet informs a unix shell to execute the file using `/usr/local/share/dotnet/dotnet run`. (Your installation directory for the `dotnet` CLI can be different on different unix or Mac distributions). The `#!` line must be the first line in the file, and the following tokens are the program to run. You need to enable the *execute* (`x`) permission on the C# file for that feature. The `#:` directives that are used in file based programs include: diff --git a/docs/csharp/tour-of-csharp/overview.md b/docs/csharp/tour-of-csharp/overview.md index 6f3b230ff958a..9ae72a263c8d2 100644 --- a/docs/csharp/tour-of-csharp/overview.md +++ b/docs/csharp/tour-of-csharp/overview.md @@ -42,7 +42,7 @@ Beginning with C# 14 and .NET 10, you can create *file based programs*, which si :::code language="csharp" source="./snippets/file-based-programs/hello-world.cs"::: -The first line of the program contains the `#!` sequence for unix shells. On any unix system, if you set the *execute* (`+x`) permission on a C# file, you can run the C# file from the command line: +The first line of the program contains the `#!` sequence for unix shells. The location of the `dotnet` CLI can vary on different distributions. On any unix system, if you set the *execute* (`+x`) permission on a C# file, you can run the C# file from the command line: ```bash ./hello-world.cs diff --git a/docs/csharp/tour-of-csharp/snippets/file-based-programs/hello-world.cs b/docs/csharp/tour-of-csharp/snippets/file-based-programs/hello-world.cs old mode 100644 new mode 100755 index 874f327348a0b..e3173e7a15eb4 --- a/docs/csharp/tour-of-csharp/snippets/file-based-programs/hello-world.cs +++ b/docs/csharp/tour-of-csharp/snippets/file-based-programs/hello-world.cs @@ -1,2 +1,2 @@ -#!/usr/bin/dotnet run +#!/usr/local/share/dotnet/dotnet run Console.WriteLine("Hello, World!"); From 8dbc9fbcc73e3a38cf28e2d2fb7b77d624114e44 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 20 Jun 2025 16:47:23 -0400 Subject: [PATCH 4/6] Include file based programs in the structure Include a description and small example of a file based program in the overview of the general structure of a C# program. --- .../fundamentals/program-structure/index.md | 25 +++++++++++++++---- .../file-based-program/hello-world.cs | 2 ++ 2 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 docs/csharp/fundamentals/program-structure/snippets/file-based-program/hello-world.cs diff --git a/docs/csharp/fundamentals/program-structure/index.md b/docs/csharp/fundamentals/program-structure/index.md index 08da9f34b780a..6cb9cc36bc487 100644 --- a/docs/csharp/fundamentals/program-structure/index.md +++ b/docs/csharp/fundamentals/program-structure/index.md @@ -1,10 +1,9 @@ --- title: "General Structure of a Program" description: Learn about the structure of a C# program by using a skeleton program that contains all the required elements for a program. -ms.date: 08/01/2024 +ms.date: 06/20/2025 helpviewer_keywords: - "C# language, program structure" -ms.assetid: 5ae964a5-0ef0-40fe-88fb-6d1793371d0d --- # General Structure of a C# Program @@ -12,18 +11,34 @@ C# programs consist of one or more files. Each file contains zero or more namesp :::code language="csharp" source="snippets/toplevel-structure/Program.cs"::: -The preceding example uses [*top-level statements*](top-level-statements.md) for the program's entry point. Only one file can have top-level statements. The program's entry point is the first line of program text in that file. In this case, it's the `Console.WriteLine("Hello world!");`. +The preceding example uses [*top-level statements*](top-level-statements.md) for the program's entry point. Only one file can have top-level statements. The program's entry point is the first text line of program text in that file. In this case, it's the `Console.WriteLine("Hello world!");`. You can also create a static method named [`Main`](main-command-line.md) as the program's entry point, as shown in the following example: :::code language="csharp" source="snippets/structure/Program.cs"::: -In that case the program will start in the first line of `Main` method, which is `Console.WriteLine("Hello world!");` +In that case the program starts in the opening brace of `Main` method, which is `Console.WriteLine("Hello world!");` + +## Building and running C# programs + +C# is a *compiled* language. In most C# programs, you use the [`dotnet build`](../../../core/tools/dotnet-build.md) command to compile a group of source files into a binary package. Then, you use the [`dotnet run`](../../../core/tools/dotnet-run.md) command to run the program. (You can simplify this process because `dotnet run` compiles the program before running it if necessary.) These tools support a rich language of configuration options and command line switches. The `dotnet` command line interpreter (CLI), which is included in the .NET SDK, provides many [tools](../../../core/tools/index.md) to generate and modify these files. + +Beginning with C# 14 and .NET 10, you can create *file based programs*, which simplifies building and running csharp programs. You use the `dotnet run` command to run a program contained in a single `*.cs` file. For example, you run the following snippet, when stored in the file `hello-world.cs`, by typing `dotnet run hello-world.cs`: + +:::code language="csharp" source="./snippets/file-based-program/hello-world.cs"::: + +The first line of the program contains the `#!` sequence for unix shells. The location of the `dotnet` CLI can vary on different distributions. On any unix system, if you set the *execute* (`+x`) permission on a C# file, you can run the C# file from the command line: + +```bash +./hello-world.cs +``` + +The source for these programs must be a single file, but otherwise all C# syntax is valid. You can use file based programs for small command-line utilities, prototypes, or other experiments. File based programs allow [preprocessor directives](../../language-reference/preprocessor-directives.md#file-based-programs) that configure the build system. ## Expressions and statements C# programs are built using *expressions* and *statements*. Expressions produce a value, and statements perform an action: -An *expression* is a combination of values, variables, operators, and method calls that evaluates to a single value. Expressions produce a result and can be used wherever a value is expected. The following examples are expressions: +An *expression* is a combination of values, variables, operators, and method calls that evaluate to a single value. Expressions produce a result and can be used wherever a value is expected. The following examples are expressions: - `42` (literal value) - `x + y` (arithmetic operation) diff --git a/docs/csharp/fundamentals/program-structure/snippets/file-based-program/hello-world.cs b/docs/csharp/fundamentals/program-structure/snippets/file-based-program/hello-world.cs new file mode 100644 index 0000000000000..e3173e7a15eb4 --- /dev/null +++ b/docs/csharp/fundamentals/program-structure/snippets/file-based-program/hello-world.cs @@ -0,0 +1,2 @@ +#!/usr/local/share/dotnet/dotnet run +Console.WriteLine("Hello, World!"); From 67f92fe81395021091011bd340c492fd12e8de45 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 20 Jun 2025 17:02:47 -0400 Subject: [PATCH 5/6] Add file based programs to the command line Add a brief description of file based programs in the command line section. --- .../program-structure/main-command-line.md | 102 ++++++++---------- 1 file changed, 43 insertions(+), 59 deletions(-) diff --git a/docs/csharp/fundamentals/program-structure/main-command-line.md b/docs/csharp/fundamentals/program-structure/main-command-line.md index ffa27d3a4ce17..fe1e7a9e1b4d5 100644 --- a/docs/csharp/fundamentals/program-structure/main-command-line.md +++ b/docs/csharp/fundamentals/program-structure/main-command-line.md @@ -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: 03/14/2024 +ms.date: 06/21/2025 f1_keywords: - "main_CSharpKeyword" - "Main" @@ -16,8 +16,7 @@ helpviewer_keywords: 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). -
Below is the example where the first line executed will display the number of command line arguments: +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). The following example displays the number of command line arguments as its first action: :::code language="csharp" source="snippets/main-command-line/TestClass.cs"::: @@ -28,15 +27,17 @@ For more information, see [Top-level statements](top-level-statements.md). :::code language="csharp" source="snippets/top-level-statements-1/Program.cs"::: +Beginning with C# 14, programs can be *file based programs*, where a single file contains the program. You run *file based programs* with the command `dotnet run `, or using the `#!/usr/local/share/dotnet/dotnet run` directive as the first line (unix shells only). + ## Overview -- The `Main` method is the entry point of an executable program; it is where the program control starts and ends. +- 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`. - `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` return type. -- If and only if `Main` returns a `Task` or `Task`, 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 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 method. +- If and only if `Main` returns a `Task` or `Task`, 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 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 method. The following list shows the most common `Main` declarations: @@ -51,7 +52,7 @@ static async Task Main(string[] args) { } static async Task Main(string[] args) { } ``` -The preceding examples don't specify an access modifier, so they're implicitly `private` by default. That's typical, but 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. It's possible to specify any explicit access modifier. > [!TIP] > The addition of `async` and `Task`, `Task` return types simplifies program code when console applications need to start and `await` asynchronous operations in `Main`. @@ -60,27 +61,27 @@ The preceding examples don't specify an access modifier, so they're implicitly ` You can return an `int` from the `Main` method by defining the method in one of the following ways: -| `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 Main()` | No use of `args`, uses `await` | -| `static async Task Main(string[] args)` | Uses `args` and `await` | +| `Main` declaration | `Main` method code | +|----------------------------------------------|-----------------------------| +| `static int Main()` | No use of `args` or `await` | +| `static int Main(string[] args)` | Uses `args` but not `await` | +| `static async Task Main()` | Uses `await` but not `args` | +| `static async Task 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. +If the return value from `Main` isn't used, returning `void` or `Task` allows for slightly simpler code. -| `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` | +| `Main` declaration | `Main` method code | +|-----------------------------------------|-----------------------------| +| `static void Main()` | No use of `args` or `await` | +| `static void Main(string[] args)` | Uses `args` but not `await` | +| `static async Task Main()` | Uses `await` but not `args` | +| `static async Task Main(string[] args)` | Uses `args` and `await` | However, returning `int` or `Task` enables the program to communicate status information to other programs or scripts that invoke the executable file. The following example shows how the exit code for the process can be accessed. -This example uses [.NET Core](../../../core/introduction.md) command-line tools. If you are unfamiliar with .NET Core command-line tools, you can learn about them in this [get-started article](../../../core/tutorials/with-visual-studio-code.md). +This example uses [.NET Core](../../../core/introduction.md) command-line tools. If you're unfamiliar with .NET Core command-line tools, you can learn about them in this [get-started article](../../../core/tutorials/with-visual-studio-code.md). Create a new application by running `dotnet new console`. Modify the `Main` method in *Program.cs* as follows: @@ -94,7 +95,7 @@ You can build the application using the [dotnet CLI](../../../core/tools/dotnet. 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 will report success. However, if you change MainReturnValTest.cs to return a non-zero value and then recompile the program, subsequent execution of the PowerShell script will report failure. +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. ```powershell dotnet run @@ -114,24 +115,7 @@ Return value = 0 ### Async Main return values -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: - -```csharp -class AsyncMainReturnValTest -{ - public static int Main() - { - return AsyncConsoleWork().GetAwaiter().GetResult(); - } - - private static async Task AsyncConsoleWork() - { - return 0; - } -} -``` - -This boilerplate code can be replaced by: +When you declare an `async` return value for `Main`, the compiler generates the boilerplate code for calling asynchronous methods in `Main`: :::code language="csharp" source="snippets/main-arguments/Program.cs" id="AsyncMain"::: @@ -153,21 +137,21 @@ When the application entry point returns a `Task` or `Task`, the compiler g You can send arguments to the `Main` method by defining the method in one of the following ways: -| `Main` declaration | `Main` method code | -|----------------------------------------------|------------------------------------| -| `static void Main(string[] args)` | No return value, no use of `await` | -| `static int Main(string[] args)` | Return value, no use of `await` | -| `static async Task Main(string[] args)` | No return value, uses `await` | -| `static async Task Main(string[] args)` | Return value, uses `await` | +| `Main` declaration | `Main` method code | +|----------------------------------------------|-----------------------------------------| +| `static void Main(string[] args)` | No return value or `await` | +| `static int Main(string[] args)` | Returns a value but doesn't use `await` | +| `static async Task Main(string[] args)` | Uses `await` but doesn't return a value | +| `static async Task Main(string[] args)` | Return a value and uses `await` | -If the arguments are not used, you can omit `args` from the method declaration for slightly simpler code: +If the arguments aren't used, you can omit `args` from the method declaration for slightly simpler code: -| `Main` declaration | `Main` method code | -|---------------------------------|------------------------------------| -| `static void Main()` | No return value, no use of `await` | -| `static int Main()` | Return value, no use of `await` | -| `static async Task Main()` | No return value, uses `await` | -| `static async Task Main()` | Return value, uses `await` | +| `Main` declaration | `Main` method code | +|---------------------------------|-----------------------------------------| +| `static void Main()` | No return value or `await` | +| `static int Main()` | Returns a value but doesn't use `await` | +| `static async Task Main()` | Uses `await` but doesn't return a value | +| `static async Task Main()` | Returns a value and uses `await` | > [!NOTE] > You can also use or 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. @@ -185,7 +169,7 @@ You can also convert the string arguments to numeric types by using the - If arguments are provided (`args.Length` is greater than 0) program tries to convert the input arguments to numbers. This will throw an exception if the argument is not a number.
- 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 comparing length of `args` argument to `0` and displays the help if no arguments are found.
+ 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.
+ 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. -3. Enter the following command to compile the application. +3. To compile the application, enter the following command: `dotnet build` - If your application has no compilation errors, an executable file that's named *Factorial.exe* is created. + 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: From c516eb9ebb5daca7f0e3e12d178aab34956ccc99 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 23 Jun 2025 12:05:08 -0400 Subject: [PATCH 6/6] Updates on top level statements Final edits. --- .../fundamentals/program-structure/main-command-line.md | 9 +++------ .../program-structure/top-level-statements.md | 4 +++- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/csharp/fundamentals/program-structure/main-command-line.md b/docs/csharp/fundamentals/program-structure/main-command-line.md index fe1e7a9e1b4d5..1fd1bb52e4755 100644 --- a/docs/csharp/fundamentals/program-structure/main-command-line.md +++ b/docs/csharp/fundamentals/program-structure/main-command-line.md @@ -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/21/2025 +ms.date: 06/23/2025 f1_keywords: - "main_CSharpKeyword" - "Main" @@ -20,14 +20,11 @@ There can only be one entry point in a C# program. If you have more than one cla :::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. 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`): :::code language="csharp" source="snippets/top-level-statements-1/Program.cs"::: -Beginning with C# 14, programs can be *file based programs*, where a single file contains the program. You run *file based programs* with the command `dotnet run `, 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 programs*](./index.md#building-and-running-c-programs), where a single file contains the program. You run *file based programs* with the command `dotnet run `, or using the `#!/usr/local/share/dotnet/dotnet run` directive as the first line (unix shells only). ## Overview diff --git a/docs/csharp/fundamentals/program-structure/top-level-statements.md b/docs/csharp/fundamentals/program-structure/top-level-statements.md index 2d9889234055e..685daa1814d0e 100644 --- a/docs/csharp/fundamentals/program-structure/top-level-statements.md +++ b/docs/csharp/fundamentals/program-structure/top-level-statements.md @@ -1,7 +1,7 @@ --- title: "Top-level statements - programs without Main methods" description: Learn about top-level statements. You can create programs without the ceremony of a Program class and a Main method. -ms.date: 11/22/2024 +ms.date: 06/23/2025 helpviewer_keywords: - "C# language, top-level statements" - "C# language, Main method" @@ -85,6 +85,8 @@ The compiler generates a method to serve as the program entry point for a projec | `return` | `static int Main(string[] args)` | | No `await` or `return` | `static void Main(string[] args)` | +Beginning with C# 14, programs can be [*file based programs*](./index.md#building-and-running-c-programs), where a single file contains the program. You run *file based programs* with the command `dotnet run `, or using the `#!/usr/local/share/dotnet/dotnet run` directive as the first line (unix shells only). + ## C# language specification [!INCLUDE[CSharplangspec](~/includes/csharplangspec-md.md)]