Main() 和命令行参数

Main 方法是 C# 应用程序的入口点。 Main 方法是应用程序启动后调用的第一个方法。

C# 程序中只能有一个入口点。 如果多个类包含 Main 方法,必须使用 StartupObject 编译器选项来编译程序,以指定将哪个 Main 方法用作入口点。 有关详细信息,请参阅 StartupObject(C# 编译器选项)。 以下示例将命令行参数的数量显示为其第一个动作:

class TestClass
{
    static void Main(string[] args)
    {
        Console.WriteLine(args.Length);
    }
}

还可以在一个文件中使用顶级语句作为应用程序的入口点。 与 Main 方法一样,顶级语句还可以返回值和访问命令行参数。 有关详细信息,请参阅顶级语句。 以下示例使用 foreach 循环显示使用 args 变量的命令行参数,并在程序末尾返回成功代码(0):

using System.Text;

StringBuilder builder = new();
builder.AppendLine("The following arguments are passed:");

foreach (var arg in args)
{
    builder.AppendLine($"Argument={arg}");
}

Console.WriteLine(builder.ToString());

return 0;

从 C# 14 开始,程序可以是 基于文件的程序,其中单个文件包含该程序。 使用命令运行dotnet run <file.cs>,或者在第一行使用#!/usr/local/share/dotnet/dotnet run指令(仅限 Unix Shell)。

概述

  • 该方法 Main 是可执行程序的入口点;它是程序控件开始和结束的位置。
  • Main 必须在类或结构中进行声明。 封闭 class 可以是 static
  • Main 必须为 static
  • Main 可以具有任何访问修饰符file 除外)。
  • Main 的返回类型可以是 voidintTaskTask<int>
  • 当且仅当Main返回TaskTask<int>时,声明Main可以包含async修饰符。 此规则专门排除方法 async void Main
  • 使用或不使用包含命令行自变量的 Main 参数声明 string[] 方法都行。 使用 Visual Studio 创建 Windows 应用程序时,可以手动添加此形参,也可以使用 GetCommandLineArgs() 方法来获取命令行实参。 参数被读取为从零开始编制索引的命令行自变量。 与 C 和 C++ 不同,程序的名称不会被视为数组中的 args 第一个命令行参数,而是该方法的第一个元素 GetCommandLineArgs()

以下列表显示了最常见的 Main 声明:

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) { }

前面的示例未指定访问修饰符,因此默认为隐式 private。 可以指定任何显式访问修饰符。

提示

添加 asyncTaskTask<int> 返回类型可简化控制台应用程序需要启动时的程序代码,以及 await 中的 Main 异步操作。

Main() 返回值

可以通过以下方式之一定义方法,以从 int 方法返回 Main

Main 声明 Main 方法代码
static int Main() 不使用 argsawait
static int Main(string[] args) 使用args但不使用await
static async Task<int> Main() 使用await但不使用args
static async Task<int> Main(string[] args) 使用 argsawait

如果不使用Main的返回值,可以通过返回voidTask来使代码稍微简化。

Main 声明 Main 方法代码
static void Main() 不使用 argsawait
static void Main(string[] args) 使用args但不使用await
static async Task Main() 使用await但不使用args
static async Task Main(string[] args) 使用 argsawait

但是,返回 intTask<int> 可使程序将状态信息传递给调用可执行文件的其他程序或脚本。

下面的示例演示了如何访问进程的退出代码。

此示例使用 .NET Core 命令行工具。 如果不熟悉 .NET Core 命令行工具,可以在本 入门文章中了解它们。

通过运行 dotnet new console 创建新的应用程序。 修改 Program.cs 中的 Main 方法,如下所示:

class MainReturnValTest
{
    static int Main()
    {
        //...
        return 0;
    }
}

请记住将此程序另存为 MainReturnValTest.cs

在 Windows 中执行程序时,从 Main 函数返回的任何值都存储在环境变量中。 可使用批处理文件中的 ERRORLEVEL 或 PowerShell 中的 $LastExitCode 来检索此环境变量。

可使用 dotnet CLIdotnet build 命令生成应用程序。

接下来,创建一个 PowerShell 脚本来运行应用程序并显示结果。 将以下代码粘贴到文本文件中,并在包含该项目的文件夹中将其另存为 test.ps1。 可通过在 PowerShell 提示符下键入 test.ps1 来运行 PowerShell 脚本。

由于代码返回零,因此批处理文件报告成功。 但是,如果更改MainReturnValTest.cs以返回非零值,然后重新编译程序,则 PowerShell 脚本的后续执行将报告失败。

dotnet run
if ($LastExitCode -eq 0) {
    Write-Host "Execution succeeded"
} else
{
    Write-Host "Execution Failed"
}
Write-Host "Return value = " $LastExitCode
Execution succeeded
Return value = 0

Async Main 返回值

声明 asyncMain 返回值时,编译器会生成样本代码,用于调用 Main 中的异步方法:

class Program
{
    static async Task<int> Main(string[] args)
    {
        return await AsyncConsoleWork();
    }

    private static async Task<int> AsyncConsoleWork()
    {
        return 0;
    }
}

在这两个示例中,程序的主体都在 AsyncConsoleWork() 方法的正文中。

Main 声明为 async 的优点是,编译器始终生成正确的代码。

当应用程序入口点返回 TaskTask<int> 时,编译器生成一个新的入口点,该入口点调用应用程序代码中声明的入口点方法。 假设此入口点名为 $GeneratedMain,编译器将为这些入口点生成以下代码:

  • static Task Main() 导致编译器发出 private static void $GeneratedMain() => Main().GetAwaiter().GetResult(); 的等效项
  • static Task Main(string[]) 导致编译器发出 private static void $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult(); 的等效项
  • static Task<int> Main() 导致编译器发出 private static int $GeneratedMain() => Main().GetAwaiter().GetResult(); 的等效项
  • static Task<int> Main(string[]) 导致编译器发出 private static int $GeneratedMain(string[] args) => Main(args).GetAwaiter().GetResult(); 的等效项

注意

如果示例在 async 方法上使用 Main 修饰符,则编译器将生成相同的代码。

命令行自变量

可以通过以下方式之一定义方法来将自变量发送到 Main 方法:

Main 声明 Main 方法代码
static void Main(string[] args) 无返回值或 await
static int Main(string[] args) 返回一个值,但不使用 await
static async Task Main(string[] args) 使用 await 但不返回值
static async Task<int> Main(string[] args) 返回值并使用 await

如果未使用参数,可以从方法声明中省略 args,这样代码会略微更简单一些:

Main 声明 Main 方法代码
static void Main() 无返回值或 await
static int Main() 返回一个值,但不使用 await
static async Task Main() 使用 await 但不返回值
static async Task<int> Main() 返回一个值并使用await

注意

还可使用 Environment.CommandLineEnvironment.GetCommandLineArgs 从控制台或 Windows 窗体应用程序的任意位置访问命令行参数。 若要在 Windows 窗体应用程序的 Main 方法中启用命令行参数,必须手动修改 Main 的声明。 Windows 窗体设计器生成的代码创建没有输入参数的 Main

Main 方法的参数是一个表示命令行参数的 String 数组。 通常,通过测试 Length 属性来确定参数是否存在,例如:

if (args.Length == 0)
{
    System.Console.WriteLine("Please enter a numeric argument.");
    return 1;
}

提示

args 数组不能为 null。 因此,无需进行 null 检查即可放心地访问 Length 属性。

还可以使用 Convert 类或 Parse 方法将字符串参数转换为数字类型。 例如,以下语句使用 string 方法将 long 转换为 Parse 数字:

long num = Int64.Parse(args[0]);

也可以使用 C# 类型 long,该类型是 Int64 的别名:

long num = long.Parse(args[0]);

还可以使用 Convert 类方法 ToInt64 来执行同样的操作:

long num = Convert.ToInt64(s);

有关详细信息,请参阅 ParseConvert

提示

分析命令行参数可能比较复杂。 请考虑使用 System.CommandLine 库(目前为 beta 版)来简化该过程。

以下示例演示如何在控制台应用程序中使用命令行参数。 应用程序在运行时获取一个参数,将该参数转换为整数,并计算数字的阶乘。 如果未提供任何参数,则应用程序会发出一条消息,说明程序的正确用法。

若要在命令提示符下编译并运行该应用程序,请按照下列步骤操作:

  1. 将以下代码粘贴到任何文本编辑器,然后将该文件保存为名为“Factorial.cs”的文本文件。

    public class Functions
    {
        public static long Factorial(int n)
        {
            // Test for invalid input.
            if ((n < 0) || (n > 20))
            {
                return -1;
            }
    
            // Calculate the factorial iteratively rather than recursively.
            long tempResult = 1;
            for (int i = 1; i <= n; i++)
            {
                tempResult *= i;
            }
            return tempResult;
        }
    }
    
    class MainClass
    {
        static int Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine("Please enter a numeric argument.");
                Console.WriteLine("Usage: Factorial <num>");
                return 1;
            }
    
            int num;
            bool test = int.TryParse(args[0], out num);
            if (!test)
            {
                Console.WriteLine("Please enter a numeric argument.");
                Console.WriteLine("Usage: Factorial <num>");
                return 1;
            }
    
            long result = Functions.Factorial(num);
    
            if (result == -1)
                Console.WriteLine("Input must be >= 0 and <= 20.");
            else
                Console.WriteLine($"The Factorial of {num} is {result}.");
    
            return 0;
        }
    }
    

    Main 方法的开头,程序会测试是否提供了输入参数,通过将 args 参数的长度与 0 进行比较,如果没有找到任何参数,则显示帮助信息。
    如果提供了参数(args.Length 大于 0),程序将尝试将输入参数转换为数字。 如果参数不是数字,此示例将引发异常。
    计算阶乘后(存储在类型为 resultlong 变量中),将根据 result 变量打印详细结果。

  2. 从“开始”屏幕或“开始”菜单中,打开 Visual Studio“开发人员命令提示”窗口,然后导航到包含你创建的文件的文件夹。

  3. 若要编译应用程序,请输入以下命令:

    dotnet build

    如果应用程序没有编译错误,则会创建名为 Factorial.dll 的二进制文件。

  4. 输入以下命令以计算 3 的阶乘:

    dotnet run -- 3

  5. 如果在命令行中输入 3 作为程序的参数,输出将读取:The factorial of 3 is 6.

注意

在 Visual Studio 中运行应用程序时,可在“项目设计器”->“调试”页中指定命令行参数。

C# 语言规范

有关详细信息,请参阅 C# 语言规范。 该语言规范是 C# 语法和用法的权威资料。

另请参阅