Linqで*=?形式の文字列をparseしてクラスのプロパティを後からいぢる

# はじめに
少しLinqに慣れてきたので、今までゴリゴリと書いていた文字列のパーズをLinqでやってみた。

## やりたいこと
- クラスの一部のプロパティの値を設定ファイルから代入したい
- 設定ファイルは**シロート**が直接いぢるため、xmlとかjsonみたいなのはダメ

## 対象クラス
```
public class Hoge
{
    public double A{set;get;}
    public double B{set;get;}
}

var Hoge[] = new Hoge[]
{
    new Hoge{A=1.0,B=2.0},
    new Hoge{A=2.0,B=3.0},
}
```
対象のクラスは、配列状に並んでいて、後述のindexで識別します。
型はdouble固定っす！

## 設定ファイルのフォーマット
xmlなんて見ただけで寝込んでしまうような人々のために、こんなフォーマットを考えてみる。

[設定ブロック],[設定ブロック],・・・・・
[設定ブロック] : [プロパティ名#インデックス = 数値]
↓こんな感じ
```
hoge#1=2.3,piyo=1e-3,poyo=1000
```

(こういうのって、どうやって現すのがいいのか教えてください）
## 実装方針
こんなフォーマットの文字列を受け取って、最終的には対象クラスのプロパティを「数値」で上書きすることを目指します。
### 文字列でメンバーにアクセスできる仕組みを整える
まずは、対象クラスに文字列でアクセスできる仕組みを作り込みます。
ここは、みんな大好きRefrectionで行きます。（というか、動的コード生成とかムリっす）
想定される用途では、アプリ立ち上げ時だけ、この仕組みが動けばいいのでパフォーマンスは無視の方向で。

名前でアクセスする仕組みを持つ基底クラス
[toki shirazuさん](http://qiita.com/tokishirazu)の[プロパティ名でプロパティにアクセスする](http://qiita.com/tokishirazu/items/66a25331d4c78980366e)を参考にほぼそのまま使わせていただきました。
これを継承しておけば、インデクサでも、Set~,Get~でも文字列でアクセスできるようになります。

```
namespace Settings
{
	abstract class ConditionBase
	{
		/// <summary>
		/// プロパティとフィールドの値を名前指定で取得する
		/// </summary>
		/// <param name="Name">プロパティかフィールドの名前</param>
		/// <returns>プロパティかフィールドの値</returns>
		public object GetValueByName(string Name)
		{
			var prop = this.GetType().GetProperty(Name);
			if (prop != null)
			{
				return prop.GetValue(this);
			}
			var fld = this.GetType().GetField(Name);
			if (fld != null)
			{
				return fld.GetValue(this);
			}
			return null;
		}

		/// <summary>
		/// プロパティとフィールドに名前指定で代入する
		/// </summary>
		/// <param name="Name">プロパティかフィールドの名前</param>
		/// <returns>プロパティかフィールドの値</returns>
		public void SetValueByName(string Name, object value)
		{
			var prop = this.GetType().GetProperty(Name);
			if (prop != null)
			{
				prop.SetValue(this, value);
                                return;
			}
			var fld = this.GetType().GetField(Name);
			if (fld != null)
			{
				fld.SetValue(this, value);
                                return;
			}
		}

		/// <summary>
		/// プロパティとフィールドの名前指定アクセス用インデクサ
		/// </summary>
		/// <param name="Name">プロパティかフィールドの名前</param>
		/// <returns>プロパティかフィールドの値</returns>
		public object this[string name]
		{
			get
			{
				return GetValueByName(name);
			}
			set
			{
				SetValueByName(name, value);
			}
		}
	}
}
```
### 文字列をparseする
この文字列は、**シロート**の人間様が編集するので、なるべくエラー処理をしたいが、人間の注意力に期待をして、あまりがちがちにはしない。
エラーに対する対応は以下の通り

- 表示不可能な文字は、Trimしてしまう
- スペースは、Trimしてしまう
- 区切り文字は','のみ
- 設定ブロック内の'='は1個だけ許容（それ以外だったらその設定ブロックごと無視する）
- 設定ブロック内の'#'は0個から1個のみ許容（それ以外だったらその設定ブロックごと無視する）
- 設定ブロック内の'#'が0個のときはインデックス=0とする
- 名前に変な文字列が含まれていても、GetValueByNameで該当名称が見つからない場合はなにもしないので、目くじら立てない
- インデックスや数値は、*.TryParseで失敗したら、その設定ブロックごと無視する

こんな感じで実装
設定ブロック毎に、情報を整理して詰め込んだConditionChangerのリストを返します。

```
namespace Settings
{
	public class ConditionChanger
	{
		public string name;
		public int index;
		public double value;
	}
	public static class Parser
	{
		static List<ConditionChanger> KeyValueParser(string src)
		{
           var sw = new System.Diagnostics.Stopwatch();
            sw.Start();
            if (src == null) return new List<ConditionChanger>();
            return string.Concat(
                // コントロール文字列じゃなくて
                src.Where(c => !char.IsControl(c))
                // スペースじゃない
                .Where(c => c != ' ')
                // 文字を集めて再文字列化
                )
            // したものをコンマで区切って
            .Split(',')
            // =がひとつだけ含まれる要素
            .Where(x => x.Count(y => y == '=') == 1)
            // #がひとつ以内だけ含まれる要素
            .Where(x => x.Count(y => y == '#') <= 1)
            // をゴニョｇニョする
            .Select(x =>
                {
                    // 要素の文字列を=と#で分割したListを生成する
                    var z = x.Split('=', '#').ToList();
                    // 要素数が3未満
                    // （Whereで=が1個しかない要素しかここまで来ないから#がない場合)
                    // ならまんなかに"0"を挿入
                    if (z.Count < 3) z.Insert(1, "0");
                    int index;
                    double value;
                    // 失敗したらnullを返しといて後でフィルタリング
                    if (!int.TryParse(z[1], out index)) return null;
                    if (!double.TryParse(z[2], out value)) return null;
                    // Listを返す
                    return new ConditionChanger
                    {
                        name = z[0],
                        index = index,
                        value = value
                    };
                }
            )
            // nullじゃないやつ
            .Where(x => x != null)
            // のListを生成
            .ToList();
		}
	}
}

```

###使い方
こんな感じで使います。

```
        class Condition : ConditionBase
        {
            public double A { set; get; }
            public double B { set; get; }
        }

        static void Main(string[] args)
        {
            var cnd = new Condition[] {
                new Condition { A = 1.0, B = 2.0 },
                new Condition { A = 10.0, Bf = 20.0},
            };

            string src = "A=100.0,B=0.001";

            var keyvalue = Settings.Parser.KeyValueParser(src);

            foreach (var kv in keyvalue)
            {
                if (kv.index < 0 || kv.index > cnd.Length - 1) continue;
                cnd[kv.index][kv.name] = kv.value;
            }
        }
```
インデックスの扱いが野暮ったくて、気に入らないところもありますが、やりたいことはできたようです。

## 今後の課題
- double固定をなんとかしたい
- インデックスの扱いをスタイリッシュにしたい
- 変換失敗の扱いをもっとスタイリッシュにしたい

## まとめ
- Linqおもしろい
- リフレクションおもしろい
- **シロート**相手をすると、頭を使わなきゃいけない