データ型クラスのプロパティメンバを、リストのようにインデクサでアクセス出来るようにしたい。
題材
下記のUserInfoクラスのプロパティを、インデクサでアクセス出来るようにする。
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;}
}
前準備 添字でアクセス
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クラスに追加
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インターフェイスを作成。
GetEnumeratorを実装することで、列挙型として扱えるようになります。
// 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();
}
}
全体像
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();
}
}
結果
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でのループが出来るようになりました。
出来ましたが、手順が多く冗長な気がします。
もっといいやり方があれば教えて頂けるとありがたいです。