rksoftware

Visual Studio とか C# とかが好きです

Generic Host でコマンドライン引数を扱う

最近コマンドライン引数をいろいろ頑張っていますが、実は標準の Generic Host ではデフォルトでコマンドライン引数を扱う機能があります。

こんなコードになります。

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using IHost host = new HostBuilder()
    .ConfigureServices((_, services) => services.AddSingleton<IService, Service>())
    .ConfigureAppConfiguration(config => config.AddCommandLine(args))
    .Build();
var service = host.Services.GetService<IService>();
service.Run();

interface IService
{
    string Run();
}

class Service : IService
{
    IConfiguration configuration;
    public Service(IConfiguration configuration) => this.configuration = configuration;
    public string Run() => configuration["c"];
}

実際に引数の解釈を行う部分は次の一文です。

.ConfigureAppConfiguration(config => config.AddCommandLine(args))

これで IConfiguration の中に引数を解釈したキーと値が文字列をキーとしたインデクサで取得できるように格納されます。
使う際にはコンストラクタ引数に IConfiguration 型の引数を設定しておけばインジェクションされます。次のように。簡単ですね。

IConfiguration configuration;
public Service(IConfiguration configuration) => this.configuration = configuration;

で、使う際にはインデクサでキーを指定します。次の例では "c" というキーの値を取得しています。

public string Run() => configuration["c"];

または CreateDefaultBuilder でも内部で同じことをしてくれます。

using IHost host = Host
    .CreateDefaultBuilder(args)
    .ConfigureServices((_, services) => services.AddSingleton<IService, Service>())
    .Build();

■ キーと値

この素晴らしい便利機能ですが、引数を何でもはキーと値で持ってくれはしません。キーと値でセットで指定されている引数だけです。

前述のドキュメントによると次のような形式がキーと値のセットとして扱われるそうです。

--alt3=value3
/alt4=value4
--alt5 value5
/alt6 value6

最近よく私がコマンドライン引数を扱うテストで使っている次の引数を与えると

<コマンド> aparam -a aopt -d -b bopt bparam /c copt

次の一組しか保持してくれませんでした。(元の配列データ自体は別途保持しているので失われはしませんが)

キー
c copt

ちょっと扱いにくい気がします。