ホームに戻る
出典 :
Reflection(リフレクション)を使ってプロパティを取得する [C#] | JOHOBASE
関連 :
ジェネリックプログラミング
目次 :

リフレクションとは

.NETに実装されている、アセンブリに含まれる型情報や、型の内部情報を取得できる仕組みのこと。
プログラムの実行時に取得した型情報を用いて、メタプログラミングが実現できる。

GetProperties() : プロパティの一覧を取得する

System.Type.GetProperties() メソッドで、プロパティの一覧( System.Reflection.PropertyInfo[] )を取得することができる。
List<string> stringList = new List<string>(); Type type = stringList.GetType(); // List<string> オブジェクトのプロパティ一覧を取得 PropertyInfo[] propertyInfos = type.GetProperties();
取得できるプロパティは引数( System.Reflection.BindingFlags 列挙型)で指定でき、引数を省略した場合はすべてのパブリックプロパティとなる。
重複指定可能。
例) インスタンスプロパティかつパブリック : Instance | Public (ビット和)を指定
引数 戻り値
BindingFlags.Public パブリックプロパティ (デフォルト)
BindingFlags.NonPublic パブリックでないプロパティ
BindingFlags.Instance インスタンスプロパティ
BindingFlags.Static 静的プロパティ

GetProperty() : 特定のプロパティを取得する

System.Type.GetProperty() メソッドでは、引数に指定された定義名のプロパティ情報( System.Reflection.PropertyInfo )を取得できる。
DataSet dataSet = new DataSet("SampleDataSet"); Type type = dataSet.GetType(); // DataSet クラスの Tables プロパティの情報を取得 PropertyInfo propertyInfo = type.GetProperty("Tables");
第2引数は GetProperties() 同様、BindingFlags 列挙型でプロパティの種別を指定する。(省略時はパブリックプロパティ)

GetValue() / SetValue() : 値の取得・更新

System.Reflection.PropertyInfo の GetValue() 、SetValue() メソッドでは、プロパティの値を取得、または更新することができる。
ここで、仮に型情報を特定のインスタンスから得たとしても、PropertyInfo はあくまで型情報に基づくもので特定のインスタンスには関連づかない。
インスタンスの特定は、GetValue() 、SetValue() の引数によって行う。

応用

シリアライズが可能でないオブジェクトのディープコピーを作成する際に用いることができる。
(例 : ReactiveProperty<T> 、ReactivePropertySlim<T> を含むクラス)
この場合、GetProperties() で取得したプロパティ情報を走査し、それぞれに対して GetValue() で値を取得、SetValue() で書き込むこととなる。
ReactiveProperty<T> の T がメンバごとに異なる場合でも型情報を実行時に取得できるため、T の情報を事前に知っておく必要が無い。

以下の例は厳密なディープコピーとは異なるが、リフレクションを利用して、同名のプロパティを持つ別クラスのオブジェクトへの値のコピーを行っている。

コピー元 : Cl_Settings
[Serializable] class Cl_Settings { /// - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - /// public プロパティ /// - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - // 基本解像度(X)[dpi] public ushort BasicResolution_X { get; set; } = 2400; // 基本解像度(Y)[dpi] public uint BasicResolution_Y { get; set; } = 2400; : }
コピー先 : Cl_DataSource
class Cl_DataSource { /// - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - /// public プロパティ /// - = - = - = - = - = - = - = - = - = - = - = - = - = - = - = - // 基本解像度(X)[dpi] public ReactivePropertySlim<ushort> BasicResolution_X { get; } = new(); // 基本解像度(Y)[dpi] public ReactivePropertySlim<uint> BasicResolution_Y { get; } = new(); : }
コピー処理
Cl_DataSource Copy(Cl_Settings baseValues) { // 戻り値 : コピー先データ Cl_DataSource ret = new(); // 全プロパティをスキャン foreach( var prop in ret.GetType().GetProperties() ) { // コピー先の ReactivePropertySlim を取得 var obj_Dst = prop.GetValue(ret); // 値をコピー var value = baseValues.GetType().GetProperty(prop.Name).GetValue(baseValues); obj_Dst.GetType().GetProperty( "Value" ).SetValue(obj_Dst, value); } return ret; }

解説

ここでは Cl_Settings の各プロパティの値を、Cl_DataSource の同名プロパティにコピーしている。
コピー先の Cl_DataSource のプロパティは ReactivePropertySlim であり、実際に表現する値は Value に格納されている。
但し ReactivePropertySlim<T> の T はプロパティごとに異なっており、型情報が不明のままでは Value にアクセスすることができない。
しかし GetType() と GetProperty() を用いて Value を文字列として与えることで、型情報を明示せずとも Value へのアクセスが可能となる(メタプログラミング)。
一方、コピー元のプロパティは(型は違えど)コピー先と同名であり、prop.Name で取得したプロパティ名を与えることでアクセスできる。
これらの手順により、それぞれのプロパティの型や具体名を与えなくても、クラス間でのデータコピーが可能となる。
また、個々のプロパティの情報を事前に与える必要が無いため、双方のメンバが増減したり、型が変更された場合でもコピー処理を変更する必要が無い。
( コピー先が ReactivePropertySlim<T> であるという制約は存在する。)

注意が必要な点

リフレクションを用いたメタプログラミングは処理負荷が比較的高いため、パフォーマンスが重視される局面では適用できないこともある。