Help us understand the problem. What is going on with this article?

C#のプロパティを動的にアクセスする

More than 3 years have passed since last update.

動的にプロパティへアクセスする方法

クラスのプロパティへ値を設定したり読んだりする時に、プロパティ名が規則的なルールを持っていたりデータ側が持っている文字列から作成できる時があります。
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);
takanemu
ASP.Netでお仕事してます。少し前まで、WPF/C#の仕事してました。 現在は、ASP.Net Core + Vue.jsでのシステム構築を行っています。 会社では、Windows、家ではUbuntuを使ってます。 家族は、嫁とミニピンが一匹。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away