今回は、プロパティを列挙する機能と、その値を取得する機能を使って、オブジェクトの状態を丸ごと出力するメソッドを紹介します。
Sub Dump( _ ByVal writer As Diagnostics.TraceListener, ByVal descendMore As Integer, _ ByVal obj As Object, ByVal objType As Type, ByVal objName As String) If Not obj Is Nothing Then writer.WriteLine([String].Format( _ "{0} As {1} = {2}", objName, objType.Name, obj)) If descendMore <> 0 AndAlso Not TypeOf obj Is ValueType Then writer.IndentLevel += 1 Try For Each aProp As Reflection.PropertyInfo In objType.GetProperties( _ Reflection.BindingFlags.Public Or Reflection.BindingFlags.Instance) If aProp.GetIndexParameters().Length = 0 Then Dump(writer, descendMore - 1, _ aProp.GetValue(obj, Nothing), aProp.PropertyType, aProp.Name) Else writer.WriteLine([String].Format( _ "{0} is an indexed property.", aProp.Name)) End If Next aProp If TypeOf obj Is IEnumerable Then Dim itemIndex As Integer = 0 For Each anItem As Object In CType(obj, IEnumerable) If Not anItem Is Nothing Then Dump(writer, descendMore - 1, anItem, anItem.GetType(), _ [String].Format("{0}[{1}]", objName, itemIndex)) Else writer.WriteLine([String].Format( _ "{0} = Nothing", [String].Format("{0}[{1}]", objName, itemIndex))) End If itemIndex += 1 Next anItem End If Finally writer.IndentLevel -= 1 End Try End If Else writer.WriteLine([String].Format( _ "{0} As {1} = Nothing", objName, objType.Name)) End If End Sub
writer には TraceListener を継承する任意のクラスを指定します。Dump メソッドは、このリスナに対して次の形式の文字列を書き込みます。
objName As objType.Name = obj.ToString()
文字列を書き込んだあとで改行を行ないます。また、プロパティの階層を 1 段下るごとに、1 レベルのインデントを挿入します。
descendMore には 0 以上の整数を指定しなければなりません。Dump メソッドは、このパラメータで指定された階層まで、プロパティを再帰的に分析します。例えば descendMore = 2 の場合、A.B.C となります。また descendMore には、循環参照による無限ループを防ぐ意味もあります。
obj にはダンプ対象となるオブジェクトを指定します。obj に Nothing を指定してもエラーにはなりません。objType は obj の型で、ほとんどの場合 obj.GetType() で十分です。
objName にはオブジェクトの名前を指定します。String.Empty は許可されますが、Nothing は無効です。基本的にはフィールドやローカル変数の名前を指定します。
Dump メソッドが呼び出されると、まず、渡された obj に対して暗黙的に ToString() を呼び出し、その戻り値を表示します。Class では(ToString() をオーバーライドしていない限り)自身の型名が表示されるだけですが、Structure では実用的な文字列が表示されることもあります。例えば System.Drawing.Color の場合は次のようになります。
Color [A=12, R=34, G=56, B=78]
次に、obj が列挙可能(リストやディクショナリなど)であれば列挙し、個々の要素に対して Dump を呼び出します。ただし、要素が Nothing の場合は除きます。
最後に、Type.GetProperties を呼び出して、非共有パブリック プロパティの情報が含まれている配列を取得します。そしてそれぞれについて、PropertyInfo.GetValue を呼び出して値を取得するわけですが、このとき、「インデックス付きプロパティ」は対象外です。なぜならインデックスとして有効な値が何であるか(整数ならともかく文字列等の場合)全くわからないからです。