【C#】コマンドラインオプションを解析してくれるクラスを作った

# コマンドラインオプション
`application.exe config/app.conf -u username -p password -x -y`
みたいな感じでコマンドラインから起動した時に、どんな引数が渡されてるのかってのをある程度楽に解析したい。

…って時ありません？

# やりましょう

とりあえず結論…というかコードから見ていただきましょう。

```csharp:ArgumentMap.cs
public class ArgumentMap
{
    private List<string> mainArgs = new List<string>();
    private Dictionary<string, string> optionMap = new Dictionary<string, string>();
    private List<string> optionSwitches = new List<string>();

    public void Init(string[] args)
    {
        int i = 0;
        while (i < args.Length)
        {
            string arg = args[i];
            if (arg.StartsWith("-"))
            {
                if (i + 1 < args.Length)
                {
                    if (args[i + 1].StartsWith("-"))
                    {
                        this.optionSwitches.Add(args[i]);
                    }
                    else
                    {
                        this.optionMap[arg] = args[i + 1];
                        i++;
                    }
                }
            }
            else
            {
                this.mainArgs.Add(arg);
            }

            i++;
        }
    }

    public List<string> GetMainArgs()
    {
        return this.mainArgs;
    }

    public bool HasSwitch(string key)
    {
        return this.optionSwitches.Contains(key);
    }

    public string GetOption(string key, string defaultValue = null)
    {
        if (this.optionMap.ContainsKey(key))
        {
            return this.optionMap[key];
        }

        return defaultValue;
    }

    public int GetOptionInt(string key, int defaultValue)
    {
        if (this.optionMap.ContainsKey(key))
        {
            string value = this.optionMap[key];
            int intValue = defaultValue;
            if (int.TryParse(value, out intValue))
            {
                return intValue;
            }
        }

        return defaultValue;
    }

    public bool GetOptionBool(string key, bool defaultValue)
    {
        if (this.optionMap.ContainsKey(key))
        {
            string value = this.optionMap[key];
            bool boolValue = defaultValue;
            if (bool.TryParse(value, out boolValue))
            {
                return boolValue;
            }
        }

        return defaultValue;
    }
}
```

コメントは各自入れて使ってください。書くのが面倒だった。
メソッド名でだいたいわかるだろうから良いかなって。

# 使い方
冒頭で書いたのと同じように起動されたとして、
`application.exe config/app.conf -u username -p password -x -y`

まずはインスタンスを生成。

```csharp
static void main(string[] args)
{
    ArgumentMap amap = new ArgumentMap();
    amap.Init(args);
      .
      .
      .
}
```
`config/app.conf` はオプションスイッチ無しの引数、必ず指定してね！みたいな引数として使いたい場合に。
ここを取り出すには、

```csharp
List<string> mainArgs = amap.GetMainArgs();
foreach (string mainArg in mainArgs)
{
    Console.WriteLine("mainArg: {0}", mainArg);
    // mainArg: config/app.conf
}
```
みたいな。
複数あっても対応はしてるし入力された順番に格納されてるけど、そこ複数にするなら素直に次の例みたいな指定をさせたほうが良いような気もする。

`-u username -p password`は、オプションスイッチ有りの引数。
`-u` に対応している値が `username`、`-p`に対応している値が`password`といった感じの。
コレを取り出すには、

```csharp
string userName = amap.GetOption("-u", string.Empty);
Console.WriteLine("userName: {0}", userName);
// userName: username

string pass = amap.GetOption("-p", string.Empty);
Console.WriteLine("pass: {0}", pass);
// pass: password
```
みたいな。
第二引数に指定してるのは、オプションスイッチが無かった場合に返ってきてほしい値。
ここでは`string.Empty`を指定している。
ここで指定をしなかった場合は`null`が返ってくるので、受け取った側でnullチェックをすればエラー処理とかもできると思われ。
たとえば、

```csharp
string permission = amap.GetOption("-a", "user");
Console.WriteLine("permission: {0}", permission);
// permission: user

string lang = amap.GetOption("-l");
if (lang == null)
{
    Console.WriteLine("Argument error. Please specify '-l' option.");
}
```

みたいな。
ちなみに数値や真偽値として値を受け取りたい場合は、それぞれ`GetOptionInt(string key, int defaultValue)`や`GetOptionBool(string key, bool defaultValue)`で実現できる。
こちらで`defaultValue`を特に指定していないのは、流石に数値とかは既定値にしたいものに個人差があるかなあということで。

`-x -y`は、オプションスイッチ単体で意味を成すもの。
`-x`が指定されているときのみこの機能が有効、とか。

```csharp
if (amap.HasSwitch("-x"))
{
    Console.WriteLine("Enabled x.");
}
if (amap.HasSwitch("-y"))
{
    Console.WriteLine("Enabled y.");
}
if (amap.HasSwitch("-z"))
{
    Console.WriteLine("Enabled z.");
}
// Enabled x.
// Enabled y.
```

`true`か`false`で結果が返ってくるので、if文で判定して煮るなり焼くなり。

# 最後に
自分でテストしたりはまだ殆どしてないので、不具合とか結構あるかもしれないです。
あんまり期待せずに使ってください。

# 宣伝的な
今回紹介したものも含めたライブラリをgithubで公開してるので、よかったら見てやってください。
ここマズイよ―、とかありましたらpull requestなりtwitterでのご報告なりをお願いします。
他のリポジトリはあんまり見ないでいただけると嬉しいです。とりあえず投げとくか、って感じで作ってるので…。

github: https://github.com/mik-claire/MikLib
twitter: https://twitter.com/mik_claire