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
的返回类型可以是void
、int
、Task
或Task<int>
。- 当且仅当
Main
返回Task
或Task<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
。 这是典型的,但可以指定任何显式访问修饰符。
提示
添加 async
、Task
和 Task<int>
返回类型可简化控制台应用程序需要启动时的程序代码,以及 Main
中的 await
异步操作。
Main() 返回值
可以通过以下方式之一定义方法,以从 Main
方法返回 int
:
Main 声明 |
Main 方法代码 |
---|---|
static int Main() |
不使用 args 或 await |
static int Main(string[] args) |
使用 args ,不使用 await |
static async Task<int> Main() |
不使用 args ,使用 await |
static async Task<int> Main(string[] args) |
使用 args 和 await |
如果不使用 Main
的返回值,则返回 void
或 Task
可使代码变得略微简单。
Main 声明 |
Main 方法代码 |
---|---|
static void Main() |
不使用 args 或 await |
static void Main(string[] args) |
使用 args ,不使用 await |
static async Task Main() |
不使用 args ,使用 await |
static async Task Main(string[] args) |
使用 args 和 await |
但是,返回 int
或 Task<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 返回值
声明 Main
的 async
返回值时,编译器会生成样本代码,用于调用 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
的优点是,编译器始终生成正确的代码。
当应用程序入口点返回 Task
或 Task<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.CommandLine 或 Environment.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);
提示
分析命令行参数可能比较复杂。 请考虑使用 System.CommandLine 库(目前为 beta 版)来简化该过程。
以下示例演示如何在控制台应用程序中使用命令行参数。 应用程序在运行时获取一个参数,将该参数转换为整数,并计算数字的阶乘。 如果未提供任何参数,则应用程序会发出一条消息,说明程序的正确用法。
若要在命令提示符下编译并运行该应用程序,请按照下列步骤操作:
将以下代码粘贴到任何文本编辑器,然后将该文件保存为名为“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.
从“开始”屏幕或“开始”菜单中,打开 Visual Studio“开发人员命令提示”窗口,然后导航到包含你创建的文件的文件夹。
输入以下命令以编译应用程序。
dotnet build
如果应用程序不存在编译错误,则会创建一个名为“Factorial.exe”的可执行文件。
输入以下命令以计算 3 的阶乘:
dotnet run -- 3
该命令将生成以下输出:
The factorial of 3 is 6.
注意
在 Visual Studio 中运行应用程序时,可在“项目设计器”->“调试”页中指定命令行参数。
C# 语言规范
有关详细信息,请参阅 C# 语言规范。 该语言规范是 C# 语法和用法的权威资料。