Main() 和命令行参数

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

C# 程序中只能有一个入口点。 如果多个类包含 Main 方法,必须使用 StartupObject 编译器选项来编译程序,以指定将哪个 Main 方法用作入口点。 有关详细信息,请参阅 StartupObject(C# 编译器选项)

class TestClass
{
    static void Main(string[] args)
    {
        // Display the number of command line arguments.
        Console.WriteLine(args.Length);
    }
}

还可以在一个文件中使用顶级语句作为应用程序的入口点。 与 Main 方法一样,顶级语句还可以返回值和访问命令行参数。 有关详细信息,请参阅顶级语句

using System.Text;

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

// Display the command line arguments using the args variable.
foreach (var arg in args)
{
    builder.AppendLine($"Argument={arg}");
}

Console.WriteLine(builder.ToString());

// Return a success code.
return 0;

概述

  • Main 方法是可执行程序的入口点,也是程序控制开始和结束的位置。
  • Main 必须在类或结构中进行声明。 封闭 class 可以是 static
  • Main 必须为 static
  • Main 可以具有任何访问修饰符file 除外)。
  • Main 的返回类型可以是 voidintTaskTask<int>
  • 当且仅当 Main 返回 TaskTask<int> 时,Main 的声明可包括 async 修饰符。 这明确排除了 async void Main 方法。
  • 使用或不使用包含命令行自变量的 string[] 参数声明 Main 方法都行。 使用 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> 返回类型可简化控制台应用程序需要启动时的程序代码,以及 Main 中的 await 异步操作。

Main() 返回值

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

Main 声明 Main 方法代码
static int Main() 不使用 argsawait
static int Main(string[] args) 使用 args,不使用 await
static async Task<int> Main() 不使用 args,使用 await
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() 不使用 args,使用 await
static async Task Main(string[] args) 使用 argsawait

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

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

此示例使用 .NET Core 命令行工具。 如果不熟悉 .NET Core 命令行工具,可通过本入门文章进行了解。

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

// Save this program as MainReturnValTest.cs.
class MainReturnValTest
{
    static int Main()
    {
        //...
        return 0;
    }
}

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

可使用 dotnet CLI dotnet 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 返回值

声明 Mainasync 返回值时,编译器会生成样本代码,用于调用 Main 中的异步方法。 如果未指定 async 关键字,则需要自行编写该代码,如以下示例所示。 示例中的代码可确保程序一直运行,直到异步操作完成:

class AsyncMainReturnValTest
{
    public static int Main()
    {
        return AsyncConsoleWork().GetAwaiter().GetResult();
    }

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

该样本代码可替换为:

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

    private static async Task<int> AsyncConsoleWork()
    {
        // main body here 
        return 0;
    }
}

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(); 的等效项

注意

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

命令行自变量

可以通过以下方式之一定义方法来将自变量发送到 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 方法将字符串参数转换为数字类型。 例如,以下语句使用 Parse 方法将 string 转换为 long 数字:

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)
        {
            // Test if input arguments were supplied.
            if (args.Length == 0)
            {
                Console.WriteLine("Please enter a numeric argument.");
                Console.WriteLine("Usage: Factorial <num>");
                return 1;
            }
    
            // Try to convert the input arguments to numbers. This will throw
            // an exception if the argument is not a number.
            // num = int.Parse(args[0]);
            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;
            }
    
            // Calculate factorial.
            long result = Functions.Factorial(num);
    
            // Print result.
            if (result == -1)
                Console.WriteLine("Input must be >= 0 and <= 20.");
            else
                Console.WriteLine($"The Factorial of {num} is {result}.");
    
            return 0;
        }
    }
    // If 3 is entered on command line, the
    // output reads: The factorial of 3 is 6.
    
  2. 从“开始”屏幕或“开始”菜单中,打开 Visual Studio“开发人员命令提示”窗口,然后导航到包含你创建的文件的文件夹。

  3. 输入以下命令以编译应用程序。

    dotnet build

    如果应用程序不存在编译错误,则会创建一个名为“Factorial.exe”的可执行文件。

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

    dotnet run -- 3

  5. 该命令将生成以下输出:The factorial of 3 is 6.

注意

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

C# 语言规范

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

请参阅