C# 使用 CommandLineUtils 簡化處理傳入參數 (上)

在「主控台應用程式」(指令視窗應用程式、Console Application) 中如果要方便讓其他使用者或程式使用,基本上都會需要讀取傳入參數,而傳入參數通常會以 string[] args 的方式存在。
但主要的程式邏輯都處理不完了,還要判斷哪個參數是哪個、可能不一致的參數順序、可選參數和必要參數等等都要從一個陣列中整理和判斷,實在有夠麻煩...

還好這些已經有網路上的大神處理過並做好套件讓我們可以方便使用了。 今天要介紹的就是筆者最常使用的幫助解析和處理參數的套件 — CommandLineUtils

註: CommandLineUtils 主要可以區分為兩種撰寫風格,分別是屬性(Attributes) 和建造者模式(Builder Pattern),筆者比較習慣使用屬性的方式,並且覺得屬性也比較好理解,下面的示範都會是使用這種方式。

安裝 CommandLineUtils

使用 NuGet 安裝 CommandLineUtils 套件,或是使用 .NET CLI 指令安裝:
	
dotnet add package McMaster.Extensions.CommandLineUtils
    

在 Program.cs 輸入以下程式碼:
    
using McMaster.Extensions.CommandLineUtils;

class Program
{
    static void Main(string[] args) => CommandLineApplication.Execute<Program>(args);

    private void OnExecute()
    {
        Console.WriteLine("Hello World!");
    }
}
    

註: 在 .NET 6 以後使用頂級語句,Program.cs 一開啟會只有兩行程式碼,直接把上面的程式貼上並取代 Program.cs 裡的程式碼即可。

使用 CommandLineApplication.Execute 時(第 5 行)程式的進入點會變成 OnExecute 或 OnExecuteAsync 方法(第 7~9 行), 如果沒有 OnExecute 或 OnExecuteAsync 的話會拋出下面的錯誤:
    
Unhandled exception. System.InvalidOperationException: No method named 'OnExecute' or 'OnExecuteAsync' could be found.
   at McMaster.Extensions.CommandLineUtils.Conventions.ExecuteMethodConvention.OnExecute(ConventionContext context, CancellationToken cancellationToken)
   at McMaster.Extensions.CommandLineUtils.Conventions.ExecuteMethodConvention.<>c__DisplayClass0_0.<<Apply>b__0>d.MoveNext()
--- End of stack trace from previous location ---
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.ExecuteAsync(String[] args, CancellationToken cancellationToken)
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.ExecuteAsync[TApp](CommandLineContext context, CancellationToken cancellationToken)
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](CommandLineContext context)
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](IConsole console, String[] args)
   at McMaster.Extensions.CommandLineUtils.CommandLineApplication.Execute[TApp](String[] args)
   at ConsoleAppCommandLineUtilsTest.Program.Main(String[] args) in C:\Users\ruyut\ConsoleAppCommandLineUtilsTest\ConsoleAppCommandLineUtilsTest\Program.cs:line 7

Process finished with exit code -532,462,766.
    

所以遇到這個錯誤記得檢查是不是少了 OnExecute 或 OnExecuteAsync 方法

使用方式

在本文中我們會需要多次呼叫 exe 檔案並帶入參數,為了測試方便,筆者會開啟指令視窗呼叫 exe 檔案。exe 檔案的預設目錄在專案下的 /bin/Debug/net6.0 資料夾內(最後一層是 .NET 版本號碼),例如專案名稱為 ConsoleAppCommandLineUtilsTest ,執行檔的位置就是:
	
ConsoleAppCommandLineUtilsTest/ConsoleAppCommandLineUtilsTest/bin/Debug/net6.0/ConsoleAppCommandLineUtilsTest.exe
    

如果沒有找到的話可以先在 .csproj 檔案的同層級路徑輸入下面的指令:
	
dotnet build
    

每次更改要測試都要記得重新 build 一次,不然執行檔不會更新

可選參數

字串

    

class Program
{
    static void Main(string[] args) => CommandLineApplication.Execute<Program>(args);

    private void OnExecute()
    {
        Console.WriteLine($"UserId: {UserId}");
    }

    [Option]
    public string UserId { get; } ="default";
}
    

預設可以使用減號加變數首字母小寫(-u)或是兩個減號加上變數小寫全名使用參數。(假設變數名稱是 User,就是 --user)
如果有多個大寫,例如上面的 UserId,則以減號區隔 (--user-id)

使用方式:

也可以自訂參數名稱
    

[Option("-i|--Id")]
public string UserId { get; } = "default";
    

原本的 -u 和 --user-id 就會失效,變成新的 -i 和 --Id

數值

int 的使用方式和字串幾乎一模一樣,這裡就簡單帶過
    

class Program
{
    static void Main(string[] args) => CommandLineApplication.Execute<Program>(args);

    private void OnExecute()
    {
        Console.WriteLine($"Count: {Count}");
    }

    [Option] 
    public int Count { get; } = 1;
}
    

範例輸出:
	
.\ConsoleAppCommandLineUtilsTest.exe
Count: 1
.\ConsoleAppCommandLineUtilsTest.exe -c 0
Count: 0
.\ConsoleAppCommandLineUtilsTest.exe --count 100
Count: 100
	

布林值

    

class Program
{
    static void Main(string[] args) => CommandLineApplication.Execute<Program>(args);

    private void OnExecute()
    {
        Console.WriteLine($"Verbose: {Verbose}");
    }

    [Option] 
    public bool Verbose { get; }
}
    

預設為 false,如果使用 -v 或是 --verbose 則會變成 true , -v 和 --verbose 後面不能帶入參數

如果使用 public bool Verbose { get; } = true; 則不管有沒有帶入參數都是 True

如果想要預設值為 true,並且可以更改, 唯一的解法就是使用 CommandOptionType.SingleValue ,只是這樣後方就一定要帶入參數
    

    [Option(CommandOptionType.SingleValue)]
    public bool Verbose { get; } = true;
    

範例輸出:
	
.\ConsoleAppCommandLineUtilsTest.exe
Verbose: True
.\ConsoleAppCommandLineUtilsTest.exe -v false
Verbose: False
.\ConsoleAppCommandLineUtilsTest.exe -v true
Verbose: True
.\ConsoleAppCommandLineUtilsTest.exe --verbose false
Verbose: False
.\ConsoleAppCommandLineUtilsTest.exe --verbose true
Verbose: True
	


參考資料:
CommandLineUtils

留言