リフレクション

C#文法 リフレクション K03_オーバーライドされたメソッドの呼び出し

これまでのオーバーロード解決のおさらい

MethodInfoを使ってメソッドを呼び出すときは、GetMethodでMethodInfoを取得する必要がありました。ただし、Sendのように同じ名前のメソッドが複数定義されている(オーバーロードされている)場合は、GetMethodの引数だけでは対象を1つに絞り込めません。

そこで、GetMethodの第2引数に対象メソッドの引数の型を表すTypeの配列を渡し、どちらのSendを取得したいのかを明示していました。引数なしのSendであればType.EmptyTypesを、string引数のSendであればnew Type[] { typeof(string) }を渡す、という書き方です。

図1: GetMethodの第2引数にType.EmptyTypesやTypeの配列を渡してオーバーロードを解決する従来のコード

取得したMethodInfoに対してInvokeを呼び出すことでメソッドを実行できますが、対象がオーバーロードされているたびに、このようにTypeの配列を用意する手間がかかっていました。

題材となるSendメソッド

今回題材にするMailServiceクラスには、引数なしのSend()と、string型の引数を1つ取るSend(string address)という、同名で2種類のSendメソッドが定義されています。

図2: MailServiceクラスに定義された引数なしのSend()とstring引数を取るSend(string address)

dynamicを使ってインスタンスを扱う

dynamicを使う場合は、GetMethodやMethodInfo、Typeの配列といった準備が一切不要になります。Activator.CreateInstanceでインスタンスを生成する際に、受け取る変数の型をdynamicにしておくだけです。

こうしておけば、instance.Send(…)のように、本当に普通のインスタンスへアクセスする感覚でそのままメソッドを呼び出すことができます。

図3: dynamic? instanceとして受け取り、instance?.Send()を呼び出しているところ

引数を渡すだけでオーバーロードが解決される

重要なのは、ここに引数を与えるかどうかだけで、呼び出すSendが自動的に決まるという点です。引数なしで書けば引数なしのSend()が、string型の引数を渡せばSend(string address)の方が、それぞれ実行時に解決されます。Type.EmptyTypesのような指定は必要ありません。

図4: instance?.Send()と、引数にアドレスを渡すinstance?.Send(“…”)を続けて記述しているところ

図5: instance?.Send(“mail@anderson02.com”)として、string引数を渡すSendの呼び出しが完成した状態

実行して確認する

この状態で実行してみます。引数なしのinstance?.Send()と、引数ありのinstance?.Send(“mail@anderson02.com”)の2行を実行すると、それぞれ対応するSendメソッドがきちんと呼び分けられていることが確認できます。

図6: instance?.Send()とinstance?.Send(“mail@anderson02.com”)の2行を実行する直前の様子

図7: 出力ウィンドウにsend!!!!とsend address!!!!mail@anderson02.comの両方が表示され、2種類のSendが正しく実行されたことが確認できる

まとめ

dynamicを使うことで、MethodInfoやType.EmptyTypesを使ったオーバーロードの解決作業なしに、通常のインスタンス操作と同じ感覚でメソッドを呼び出せるようになります。呼び出したいメソッドに合わせて引数を渡すだけで、実行時に適切なオーバーロードが自動的に選ばれる点が、dynamicを使う大きなメリットの一つです。

リフレクション

目次
はじめに
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を利用したイベントへのアクセス