動的にプロパティへアクセスする方法
クラスのプロパティへ値を設定したり読んだりする時に、プロパティ名が規則的なルールを持っていたりデータ側が持っている文字列から作成できる時があります。
switch文などを使って分岐してアクセスするのは馬鹿らしいですよね。
そういった場合には、一手間掛けてアクセスするとプログラムの見通しがグッと良くなります。
リフレクションを使ってアクセスする
最も単純な解決方法は、言語のリフレクション機能を使ってアクセスする方法です。
ただし、通常アクセスできないメソッドにアクセスできてしまうため無制限に行うものではありません。
プロパティアクセスに限定して抑制的に利用しましょう。
実行速度が非常に遅いため繰り返し呼ばれるルーチンなどでは利用しない方が良いです。
ReflectionUtility.cs
public class ReflectionUtility
{
/// <summary>
/// プロパティ情報の取得
/// </summary>
/// <param name="type">型</param>
/// <param name="name">プロパティ名</param>
/// <returns>プロパティ情報</returns>
public static PropertyInfo GetPropertyInfo(Type type, string name)
{
var property = type.GetProperty(name);
return property;
}
}
Test.cs
var property = ReflectionUtility.GetPropertyInfo(this.GetType(), "UserData" + string.Format("{0:00}", index));
property.GetMethod.Invoke(this, new object[1] { 0, });
アクセッサーを使ってアクセスする
リフレクションの動作速度が遅いというデメリットを解決する方法がアクセッサーです。
アクセッサーは、プロパティのデリゲートだけを持ったオブジェクトです。
デリゲート経由でプロパティにアクセスするため実行速度は高速でです。
(※プロパティ構文のアクセッサー宣言とは違うものです)
IAccessor.cs
public interface IAccessor
{
/// <summary>
/// 読み取り属性
/// </summary>
bool CanRead { get; }
/// <summary>
/// 書き込み属性
/// </summary>
bool CanWrite { get; }
/// <summary>
/// 値の取得
/// </summary>
/// <param name="target">インスタンス</param>
/// <returns>値</returns>
object GetValue(object target);
/// <summary>
/// 値の設定
/// </summary>
/// <param name="target">インスタンス</param>
/// <param name="value">値</param>
void SetValue(object target, object value);
}
PropertyExtension.cs
public static class PropertyExtension
{
public static IAccessor ToAccessor(this PropertyInfo pi)
{
Type getterDelegateType = typeof(Func<,>).MakeGenericType(pi.DeclaringType, pi.PropertyType);
Delegate getter = Delegate.CreateDelegate(getterDelegateType, pi.GetGetMethod());
Type setterDelegateType = typeof(Action<,>).MakeGenericType(pi.DeclaringType, pi.PropertyType);
Delegate setter = Delegate.CreateDelegate(setterDelegateType, pi.GetSetMethod());
Type accessorType = typeof(Accessor<,>).MakeGenericType(pi.DeclaringType, pi.PropertyType);
IAccessor accessor = (IAccessor)Activator.CreateInstance(accessorType, getter, setter);
return accessor;
}
}
Test.cs
var property = ReflectionUtility.GetPropertyInfo(this.GetType(), "UserData" + string.Format("{0:00}", index));
var accessor = property.ToAccessor();
accessor.SetValue(this, data);