ちょっとアリスシリーズ風に書き下し。
要は、デバッグ用に呼び出し元のクラス名を取得したいのですが、デバッグ用なので new 時にインスタンスやクラス名を渡したくない、のですね。なので、呼び出されたクラス/メソッドのほうから、こっそりと StackFrame を使って、呼び出し元のクラス名を取得するという技です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | ' 参考 ' 自分自身のクラス名とメソッド名:Gushwell's C# Dev Notes ' http://gushwell.ldblog.jp/archives/50715142.html Public Class Form1 ''' <summary> ''' アリスを作成 ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> ''' <remarks></remarks> Private Sub Button1_Click( ByVal sender As System. Object , ByVal e As System.EventArgs) Handles Button1.Click Dim alice As New Alice alice.Check() End Sub ''' <summary> ''' ロリータを作成 ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> ''' <remarks></remarks> Private Sub Button2_Click( ByVal sender As System. Object , ByVal e As System.EventArgs) Handles Button2.Click Dim lolita As New Lolita lolita.Check() End Sub End Class Public Class Person Protected _bag As Bag ''' <summary> ''' コンストラクタ ''' </summary> ''' <remarks></remarks> Public Sub New () ' バッグを作成(作成者名はバッグの内部で保存される) _bag = New Bag End Sub ''' <summary> ''' 作成者を表示 ''' </summary> ''' <remarks></remarks> Public Sub Check() MessageBox.Show( "class in " + _bag.GetClassName) End Sub End Class ''' <summary> ''' アリスクラス ''' </summary> ''' <remarks></remarks> Public Class Alice Inherits Person End Class ''' <summary> ''' ロリータクラス ''' </summary> ''' <remarks></remarks> Public Class Lolita Inherits Person ' ※本来は、クラス名を渡すべき Public Sub New () _bag = New Bag( "LOLITA" ) End Sub End Class Public Class Bag Protected _cname As String ''' <summary> ''' コンストラクタ ''' </summary> ''' <remarks></remarks> Public Sub New () Dim st As New StackTrace( False ) ' Bag -> Person -> Alice の順で 2 を指定する Dim sf As StackFrame = st.GetFrame(2) ' 呼出元を保存しておく _cname = sf.GetMethod.ReflectedType.FullName End Sub ''' <summary> ''' 本来はクラス名を渡すべき ''' </summary> ''' <param name="cname"></param> ''' <remarks></remarks> Public Sub New ( ByVal cname As String ) _cname = cname End Sub ''' <summary> ''' 設定されているクラス名を取得 ''' </summary> ''' <returns></returns> ''' <remarks></remarks> Public Function GetClassName() As String Return _cname End Function End Class |
Alice クラス内で、new Bag をしています。この Bag を生成したのは誰か?というのは、本来ならば Alice のインスタンスか、クラス名を渡す、あるいは、Bag プロパティに設定する、ということをやる必要があるのですが、これをスタックフレームを使って呼び出し元のクラス名を取得するようにします。
Bag クラスのコンストラクタの部分が少しトリッキーなことになっています。
1 2 3 4 5 6 7 8 9 | Public Sub New () Dim st As New StackTrace( False ) ' Bag -> Person -> Alice の順で 2 を指定する Dim sf As StackFrame = st.GetFrame(2) ' 呼出元を保存しておく _cname = sf.GetMethod.ReflectedType.FullName End Sub |
StackTrace クラスでスタックトレースを取得した後、GetFrame メソッドで呼び出し元を取得します。このときに、スタックの状態が、Bag -> Person -> Alice になっているので「2」を指定しています。
間に Person クラスが挟まるのは、Alice クラスとの継承関係があるからです。なので、フォームからの呼び出しをチェックする場合も、Form クラスを継承していることを考慮にいれて、GetFrame メソッドに渡す値を調節しないといけません。ややこしいですね…というか、実装依存になるので、ピンポイントでしか使えない技です。なので、素直にクラス名を渡したほうがよさそうです。
こういう場合、XmlDocument クラスのようにファクトリーパターンを使います。コンストラクタにクラス名を渡すよりは自然かも。
1 2 3 | Dim bag = Bag.CreateInstanceWithName( "Lolita" ) 'あるいは Dim bag As New Bag( "Lolita" ) |
■参考
自分自身のクラス名とメソッド名:Gushwell’s C# Dev Notes
http://gushwell.ldblog.jp/archives/50715142.html