リフレクション

C#文法 リフレクション F05_属性で取得する

図1 「属性で取得する」ボタンを追加する

属性(Attribute)とは

今回は、属性(Attribute)を使ってメンバーを取得する方法を見ていきます。これまでは、GetMethods()でクラスのメソッドをすべて取得したり、GetMethod()で特定の1つのメソッドをピンポイントに取得したりする方法を扱ってきました。ここではそれとは別に、「属性で取得する」というやり方を紹介します。

属性とは、メソッドなどの上に角括弧([ ])で記述されているもののことです。C#ではAttributeと呼ばれ、例えばConditionalAttributeのように標準で用意されているものもあります。テストコードを書くときに、メソッドの上に[Test]と記述するのもこの属性の一例です。

属性を使うと、指定した属性が付いているメンバーだけを取得する、ということができます。例えばテストツールを自作する場合、「メソッドに[Test]と書かれているものだけをテスト対象にする」「[TestMethod]と書かれているものだけを実行する」といった仕組みを作ることができます。

独自の属性(MyTestAttribute)を作る

それでは、独自の属性を作ってみましょう。属性を自作する場合は、クラスを1つ作成します。ここではMyTestAttributeという名前のクラスを作り、Attributeクラスを継承させます。中身は今回空のままにしておきます。

図2 MyTestAttributeクラスを作成し、Attributeを継承する

続けて、このクラスに対してAttributeUsage属性を付け、AttributeTargetsをMethodに指定します。これにより、「メソッドに対して使用するための属性」という制約を持たせることができます。

図3 [AttributeUsage(AttributeTargets.Method)]を指定する

ここまで作成すると、MyTestAttributeという属性がメソッドに対して使用できるようになります。

メソッドに属性を付ける

作成したMyTestAttributeを、MailServiceクラスのSend()メソッドに付けてみます。これで、Send()メソッドにはMyTestAttributeという独自の属性が付いている状態になりました。

図4 Send()メソッドに[MyTestAttribute]を付与する

GetCustomAttributeで属性を判定する

次に、「属性で取得する」ボタンのクリックイベントの中に、前回のGetMethods()で全メソッドを取得する処理をコピーして貼り付けます。この状態でそのまま実行すると、すべてのメソッドが出力されます。

ここから、今作成した属性が付いているメンバーだけを取得したい場合は、foreachでループしている変数valに対してGetCustomAttribute()を使います。この引数(ジェネリック型)にMyTestAttributeを指定し、戻り値がnullでなければ、その属性が付いているメンバーだと判定できます。

もちろん、null以外だった場合の処理はここで自由に組み立てることができます。今回は、今作成した属性が付いているメソッドの名前だけをコンソールに出力するようにします。

図5 GetCustomAttribute<MyTestAttribute>()がnullでないメンバーだけを出力する

これを実行すると、MyTestAttributeが付いているSend()だけが出力されます。

図6 実行結果:Sendのみが出力される

試しに、Send()とSetAddress()の両方にMyTestAttributeを付けて実行すると、この2つだけが出力されます。

図7 実行結果:SendとSetAddressの2件が出力される

まとめ

このように、独自のツールを作るときに「検査対象のメソッドにはこの属性を付けておく」といった取り決めをしておけば、その属性が付いているメソッドだけを取得して処理する、といった仕組みを作ることができます。属性による絞り込みは、テストツールに限らずさまざまな場面で応用できる考え方なので、ぜひ活用してみてください。

リフレクション

はじめに
A01_はじめに
A02_コースの概要
Type
B01_Typeクラスとは
B02_型からTypeを取得する
B03_自前の型からTypeを取得する
B04_インスタンスからTypeを取得する
B05_Typeのインスタンスは1つだけ
Assembly
C01_Assemblyとは
C02_Assemblyの生成_実行中のアセンブリを取得
C03_Assemblyの生成_アセンブリ名で取得
C04_Assemblyの生成_パスを指定して取得
C05_Assemblyの生成_呼び出し元を取得
C06_Assemblyの生成_型から取得
C07_すべての型を取得する
C08_publicな型だけを取得する
C09_名前を指定して型を取得する
C10_メタ情報の取得
インスタンスの生成
D01_インスタンスの生成
D02_インスタンス生成後の具体例
D03_空のコンストラクタがないとエラーになる
D04_Activatorを使ったインスタンスの生成
D05_引数ありのコンストラクタで生成する方法
ConstructorInfo
E01_コンストラクタの情報を取得
E02_パラメータの情報を取得
E03_コンストラクタの情報からインスタンスを生成する
E04_コンストラクタを検索して生成する
E05_public以外のコンストラクタを取得
E06_staticなコンストラクタがある場合の注意点
E07_コンストラクタのアクセスレベルを取得する
MethodInfo
F01_MethodInfo
F02_引数ありのメソッド呼び出し
F03_メソッドがオーバーロードされているときの指定方法
F04_GetMethods
F05_属性で取得する
F06_staticメソッドを呼び出す
F07_戻り値を取得する
PropertyInfo
G01_PropertyInfo
G02_GetProperties
G03_属性を使ったチェック
G04_汎用的なチェックメソッド
G05_CanWrite

FieldInfo
H01_FieldInfo
H02_public以外のFieldInfo

EventInfo
J01_EventInfoとは
J02_AddEventHandler
J03_EventInfoの注意点
J04_RemoveEventHandler

dynamicを利用したリフレクション
K01_dynamicを利用したリフレクション
K02_dynamicを利用したメソッドの呼び方
K03_オーバーライドされたメソッドの呼び出し
K04_メソッドの存在チェック
K05_dynamicを利用したプロパティへのアクセス
K06_dynamicを利用したフィールドへのアクセス
K07_dynamicを利用したイベントへのアクセス