<p>main函数的string[] args参数直接来自命令行输入,如dotnet run -- hello world中args为["hello","world"];手动解析易出IndexOutOfRangeException、忽略选项格式、误处理带空格路径三类错误;推荐优先使用System.CommandLine库自动处理参数解析、help提示与类型转换。</p>

main函数的string[] args参数从哪来
它直接来自你运行程序时敲的命令行,不是代码里“写出来”的。比如执行 dotnet run -- hello world,args 就是 ["hello", "world"];用 MyApp.exe -f config.json --verbose 启动,args 就是 ["-f", "config.json", "--verbose"]。
注意:.NET CLI 的 -- 之后才进 args,前面属于 dotnet 自己用;直接双击 exe 或用 start 运行时,args 完全取决于快捷方式“目标”字段或脚本里怎么写的命令。
手动解析args容易踩的三个坑
别急着写 for 循环遍历 args 拼逻辑——90% 的基础需求其实只需要判断几个开关或取一个路径,但手写很容易翻车:
-
args.Length == 0时直接访问args[0]→IndexOutOfRangeException - 把
"--output=file.txt"当成一个整体,没做.Split('=')或识别长选项格式,结果当成无参数开关处理 - 忽略空格包裹的路径:
"C:\My Folder\app.dll"在命令行里必须加引号,但进args后引号已被 shell 剥离,你看到的是"C:\My Folder\app.dll",不是"\"C:\My Folder\app.dll\""—— 别自己再加引号去 File.Exists
用System.CommandLine比手写安全得多
官方推荐、轻量、无依赖、支持自动 help 和类型转换。哪怕只用两行,也比手写 if-else 更可靠:
var rootCommand = new RootCommand("My tool");
rootCommand.AddOption(new Option<string>("--file", "Input file path"));
rootCommand.SetHandler((string file) => { Console.WriteLine($"Got: {file}"); },
rootCommand.Options.First());
await rootCommand.InvokeAsync(args);关键点:
- 它自动跳过未声明的参数(不会报错),也自动处理
--file path.txt和--file=path.txt两种写法 - 如果用户输错参数名或漏值,会打印清晰错误 + usage 提示,不用你写
Console.WriteLine("Usage: ...") - 不引入
Microsoft.Extensions.*全家桶,单个 NuGet 包System.CommandLine就够
什么时候该坚持手写而不是上库
只有两种情况值得绕开 System.CommandLine:
- 项目被锁死在 .NET Framework 4.7.2 且不能升,而
System.CommandLine最低要求 .NET Core 3.0+ - 程序就两个参数:
MyApp.exe start或MyApp.exe stop,连--help都不需要,此时三行switch (args.FirstOrDefault())更干脆
其他所有情况——尤其涉及路径、数字、布尔开关、多个可选参数——都建议用 System.CommandLine。手写解析看着简单,但边界 case(比如带等号的值、引号嵌套、空参数)实际调试起来比加一个包花的时间多得多。
真正容易被忽略的是:命令行参数本质是 untrusted input,无论用什么方式解析,对传入的 file 路径做 Path.GetFullPath() + 白名单校验,比纠结用不用第三方库重要得多。










