VB.NETでもリフレクションで(途中略)メソッドを実行したい
タイトル補足
特に需要なんてものは無かったんですが、自作の属性をメソッドに付与してその属性が付与されたメソッドをリフレクションを用いて実行時に取得して一括実行するにはどうすればいいのか気になったので調べました。
流れ
大体以下の流れで処理を記述しています。
- 自作属性を定義
- 自作属性をメソッドに付与
- クラス内のメソッドリストを取得
- メソッドリストの属性を検査
- 自作属性が付与されていたら実行
とりあえずコードを乗っけていい感じに説明的なアレをアレします。
動的
Imports System.Reflection Module Entry Sub Main() Dim targetA = New TargetClass("A") Dim targetB = New TargetClass("B") targetA.RunAll() targetB.RunAll() End Sub End Module <AttributeUsage(AttributeTargets.Method, AllowMultiple:=False)> Public Class AutoRunAttribute Inherits Attribute Public Property Rank As Integer Public Sub New() Me.Rank = 0 End Sub End Class Public Class TargetClass Private _name As String Public Sub New(name As String) _name = name End Sub <AutoRun()> Public Sub MethodA() Console.WriteLine("{0}:MethodA", _name) End Sub <AutoRun(Rank:=-10)> Public Sub MethodB() Console.WriteLine("{0}:MethodB", _name) End Sub <AutoRun(Rank:=10)> Public Sub MethodC() Console.WriteLine("{0}:MethodC", _name) End Sub <AutoRun> Public Sub MethodD() ' せっかくなのでリフレクションを用いて自分自身の名前を取得する Dim myName = MethodBase.GetCurrentMethod().Name Console.WriteLine("{0}:{1}", _name, myName) End Sub Public Sub MethodE() Console.WriteLine("無関係なメソッド") End Sub Public Sub RunAll() Dim method = GetType(TargetClass).GetMethods() Dim runnable = New List(Of RunMethodInfo)() For Each it In method Dim attr = Attribute.GetCustomAttributes(it) For Each j In attr Dim a = TryCast(j, AutoRunAttribute) If a Is Nothing Then Continue For runnable.Add(New RunMethodInfo With {.Method = it, .Rank = a.Rank}) Next Next For Each it In runnable.OrderBy(Function(a) a.Rank) it.Method.Invoke(Me, Nothing) Next End Sub Private Class RunMethodInfo Public Property Method As MethodInfo Public Property Rank As Integer End Class End Class
自作属性はSystem.Attribute
を継承して実装します。
属性に対する属性はSystem.AttributeUsage
で指定します。
属性の付与する対象はSystem.AttributeTargets
列挙体を用いて指定をします。
対象はOr
演算子で複数指定することができますのでお好きなだけ対象を設定できます。
自作属性を同じ対象に複数付与できるかどうかはAllowMultiple
プロパティで指定します。
今回は複数付与されると厄介なのでFalse
を指定してます。
自作した属性ですが、今回は実行の優先順位を表すRank
プロパティを設けています。
Rank
の値が低いほど先に実行されます。
メソッドに属性を付与する方法ですが、まぁこれはコードを見てもらったほうがわかると思います。
本題がRunAll
メソッドですが、
GetType
で自分自身のクラスのType
((正確には)のサブクラス)インスタンスをを取得Type.GetMethods
メソッドでアクセス修飾子がPublic
なメソッドをSystem.Reflection.MethodInfo
として全て取得System.Attribute.GetCustomAttributes
メソッドでMethodInfo
インスタンスから属性をぶっこぬく- ぶっこぬいてきた属性が自作属性かチェック
- 自作属性だったらとりあえずリストに格納
- ランクに応じてソートして
Methodinfo.Invoke
で実行
って流れです。
ちなみに実行結果はこんな感じです。
属性を設定していないMethodE
は実行されていません。
A:MethodB A:MethodA A:MethodD A:MethodC B:MethodB B:MethodA B:MethodD B:MethodC
実のところ発端は引数を取らないメソッドを細かく分割して定義したあげく一つのメソッドで細切れにしたメソッドをズラーっと列挙して呼び出しているコードを見てもうちょっとスマートに出来ないかなと思ったからです。 でもよくよく考えたらそもそもの設計からして間違ってる気もしますよね。
とまぁフレームワークを自作する時以外は使わなそうなお話です。 メタプログラミングを駆使すればエキセントリックなコードをいくらでも書けるので無秩序にプロジェクトにぶち込んでメンテナンス性最悪なコードを大量に生み出したいところです。
おわりのはじまり