[C#] インデクサを使って、クラスのメンバ(プロパティ)をリストのように扱う

データ型クラスのプロパティメンバを、リストのようにインデクサでアクセス出来るようにしたい。
## 題材
下記のUserInfoクラスのプロパティを、インデクサでアクセス出来るようにする。

```C#:UserInfo.cs
public class UserInfo
{
　public UserInfo(List<String> userList)
　{
　　ID=userList[0];
    Name=userList[1];
    Password=userList[2];
    email=userList[3];
  }
  public string ID{get;set;}
  public string Name{get;set;}
  public string Password{get;set;}
  public string email{get;set;}
}
```

## 前準備 添字でアクセス

```C#:userInfo.cs
public class UserInfo
{      
  public UserInfo(List<String> userList)
  {
    var properties = typeof(UserInfo).GetProperties();
    {
      for (var i = 0; i < userList.Count; i++)
      {
        var name = properties[i].Name;
        //userListでプロパティをまとめて初期化
        this[name] = userList[i];
      }
    }          
  }
  public string ID{get;set;}
  public string Name{get;set;}
  public string Password{get;set;}
  public string email{get;set;}
　private string this[string propertyName]
  {
    get => typeof(UserInfo).GetProperty(propertyName).GetValue(this).ToString();
    set => typeof(UserInfo).GetProperty(propertyName).SetValue(this, value);
  }
}

```

### 解説

#### string this[string propertyName]
このプロパティを追加することで、添字によるアクセスが可能になる。
##### get
- `typeof(UserInfo).GetProperty(propertyName).GetValue(this).ToString()`

↑GetValueの戻り値はobject型なので、string型に変換している。

##### set
- `typeof(クラス名).GetProperty(propertyName).SetValue(this,value)`

#### コンストラクタ
- `typeof(UserInfo).GetProperties()`
クラスのプロパティを配列で取得。
- `properties[i].Name`
各プロパティ名を取得出来るので、ループ処理が可能になる。

このままだとあまり意味が無いので、インデクサでアクセス出来るようにする。
## 本番1 インデクサでアクセス
下記をUserInfoクラスに追加

```C#:userInfo.cs
private List<string> NameList;
public string this[int num]
{
  get => this[NameList[num]];
  set => this[NameList[num]] = value;
}
```
NameListはプロパティ名一覧が入ったリスト(プロパティ名のインデックス用途)として使います。
#### string this[int num]
`this[string propertyName]`をラッパー。
`NameList[num]`でthis[string propertyName]の添字に変換します。

これにより、インデクサでのアクセスが出来るようになります。

ただ、これだけでは列挙型として使うことができません。

## 本番2 列挙可能にする

IEnumerableを継承し、IEnumerable<string>インターフェイスを作成。

GetEnumeratorを実装することで、列挙型として扱えるようになります。

```C#:userInfo.cs
// UsefInfoの変更、追加部分のみ記述
public class UserInfo : IEnumerable<string>
{
  //GetEnumerator実装
　public IEnumerator<string> GetEnumerator()
　{
　　for (var i = 0; i < NameList.Count; i++)
  　{
  　　yield return this[i];
　　}
　}
  //IEnumerable(非ジェネリック型)のGetEnumeratorも実装
　System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
　{
  　return this.GetEnumerator();
　}
}
```


## 全体像

```C#:UserInfo.cs
public class UserInfo : IEnumerable<string>
{      
  public UserInfo(List<String> userList)
  {
    var properties = typeof(UserInfo).GetProperties();
    NameList = new List<string>();
    //propertiesから、各Name取り出し
    foreach (var (name, index) in properties.Select((prop, index) => (prop.Name, index)))
    {
      if(index == properties.length - 1)
      {
        //配列の最後はthisプロパティなので、スキップする
        break;
      }
      //UserInfoのプロパティ名のリストを作成
      NameList.Add(name);
      //全プロパティを初期化
      this[name] = headerlist[index];
    }
  }
  public string ID{get;set;}
  public string Name{get;set;}
  public string Password{get;set;}
  public string email{get;set;}
  //プロパティのインデックス用途
  private List<string> NameList;
  
  //添字でのアクセス用
　private string this[string propertyName]
  {
    get => typeof(UserInfo).GetProperty(propertyName).GetValue(this).ToString();
    set => typeof(UserInfo).GetProperty(propertyName).SetValue(this, value);
  }
  //インデクサでのアクセス用
  public string this[int num]
  {
    get => this[NameList[num]];
    set => this[NameList[num]] = value;
  }
  //リスト型への代入用
  public List<string> ToList()
  {
    var res = new List<string>();
    NameList.ForEach(name => res.Add(this[name]));
    return res;
  }
  public IEnumerator<string> GetEnumerator()
　{
　　for (var i = 0; i < NameList.Count; i++)
　　{
　　　yield return this[i];
　　}
　}  
　System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
　{
　  return this.GetEnumerator();
　}
}
```
## 結果

```C#
var User=new UserInfo(new List<string>{"012","user","pass","xx@xx.com"});
//添字でアクセス
User[0] = "dummy";
if(User.ID == "dummy")
{
	//一致
}
Console.WriteLine(User[3]);
//xx@xx.com
    
foreach(var val in User)
{
  Console.WriteLine(val);
  //012
  //user
  //pass
  //xx@xx.com
}
var list=User.ToList()
//list:{"012","user","pass","xx@xx.com"}
```

インデクサでのアクセス、foreachでのループが出来るようになりました。
出来ましたが、手順が多く冗長な気がします。
もっといいやり方があれば教えて頂けるとありがたいです。

## 参考
[【C#】プロパティ名でプロパティにアクセスする](https://qiita.com/tokishirazu/items/66a25331d4c78980366e)