[C#]設定ファイルの読み込みと書き込み　その２

さて、そんなわけでまずiniファイルの読み込みです。

##今回読み込むサンプル
今回はサンプルとして以下のようなiniファイルとその設定クラスにしてみました。

```sample.ini
[Server_01]
TargetDirectory=\\SV01\Folder
UserName=AAA
Password=aaa
keys=1,2,3,4

[Server_02]
TargetDirectory=\\SV02\Folder
UserName=BBB
Password=bbb
keys=2,3,4,5

[Server_03]
TargetDirectory=\\SV03\Folder
UserName=CCC
Password=ccc
keys=3,4,5,6
```

```SettingInfo.cs
public class SettingInfo
{
    public string TargetDirectory { get; set; }
    public string UserName { get; set; }
    public string Password { get; set; }
    public string[] keys { get; set; }
}
```

##実装するよ！
読み込みだけを考えるとかなり簡単です。
まずはAPI呼び出し部分とそのラッパー。

```iniread.cs
[DllImport("kernel32.dll")]
private static extern int GetPrivateProfileString(
    string lpApplicationName,
    string lpKeyName,
    string lpDefault,
    StringBuilder lpReturnedstring,
    int nSize,
    string lpFileName);

public string GetIniValue(string path, string section, string key)
{
    StringBuilder sb = new StringBuilder(256);
    GetPrivateProfileString(section, key, string.Empty, sb, sb.Capacity, path);
    return sb.ToString();
}
```

実際に呼ぶときには

`setting.TargetDirectory = GetIniValue(path, "Server_01", "TargetDirectory"))`
のような感じになるわけですけど、個人的には"TargetDirectory"が気持ち悪いです。
名前付けるのが苦手なので、リファクタリングで名前変えてる間にうっかり変え損ねて・・・なんてこともありそうです。
なので、記述としては煩雑ですが、うっかり対策で以下のメソッドを使います。

```GetName.cs
public static string GetName<T>(Expression<Func<T>> e)
{
    var member = (MemberExpression)e.Body;
    return member.Member.Name;
}
```
これ思いついた人はほんとすごいと思う。
通常は取得できないプロパティの「名称」を取得してくれます。
これを使うことでリファクタリング中に名称変えてもうっかり変え忘れた！を防げます。

上記のメソッドを使った上でさきほどのiniファイルを読み込んでテキストボックスに流し込むメソッドを作ると以下のような感じ。

```ReadingIniFile.cs
private void ReadingIniFile()
{
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < 3; i++)
    {
        SettingInfo setting = new SettingInfo();

        result.AppendFormat("[Server_{0:D2}]", i + 1);
        result.Append(Environment.NewLine);

        setting.TargetDirectory 
            = GetIniValue(path, string.Format("Server_{0:d2}", i + 1), GetName(() => setting.TargetDirectory));
        result.AppendFormat("TargetDirectory:{0}", setting.TargetDirectory);
        result.Append(Environment.NewLine);

        setting.UserName 
            = GetIniValue(path, string.Format("Server_{0:d2}", i + 1), GetName(() => setting.UserName));
        result.AppendFormat("UserName:{0}", setting.UserName);
        result.Append(Environment.NewLine);

        setting.Password 
            = GetIniValue(path, string.Format("Server_{0:d2}", i + 1), GetName(() => setting.Password));
        result.AppendFormat("Password:{0}", setting.Password);
        result.Append(Environment.NewLine);

        setting.keys 
            = GetIniValue(path, string.Format("Server_{0:d2}", i + 1), GetName(() => setting.keys)).Split(',');
        result.AppendFormat("key:{0}", string.Join("、", setting.keys));
        result.Append(Environment.NewLine);
        result.Append(Environment.NewLine);
    }

    this.MainText.Text = result.ToString();
}
```
さて、これで実際読み込んでみると…？
![iniread.png](https://qiita-image-store.s3.amazonaws.com/0/46625/a486f707-6e59-072c-48d6-23b6008a2d85.png)
上手いこと読み込んでくれましたね。

##標準ini出力？
ってなんやねん？って思いませんでした？
10人に1人くらいは思ってくれてたらいいなとか思ってます。

前回の記事の通り、iniファイルは読む専門にした方がシンプルなので基本書き込みはしないことが多いんですけど、初回とかうっかり消しちゃった場合の読み込みエラー対策があってもいいかなと思うんですよ。
そういう場合、デフォルトのiniファイルを作っておいて、それをそのまま指定ディレクトリに出力するという手抜き方法を使います。
やり方はもう手抜きも手抜き。

1. Resourcesにデフォルト用のiniファイルごと登録しておく
![iniread2.png](https://qiita-image-store.s3.amazonaws.com/0/46625/b331c269-7904-5542-4369-40f150e99b03.png)


2. stringとして呼び出されるので、そのままファイルとして書き込む

```OutputDefaultIni.cs
private void OutputDefaultIni()
{
    using(StreamWriter sw = new StreamWriter(@".\default.ini"))
    {
        sw.Write(Properties.Resources.IniFileProject);
    }
}
```
清々しいほどの手抜きですが、置き換え用の文字を組み込んでおけば、SQLのBind変数のようにReplace等で扱えますから、これはこれでアリかと思ってます。

***
このままXMLでの設定に行きたいところだったんですが、次回はまたしばらく時間がかかりそうです。。