今回は、プロパティを列挙する機能と、その値を取得する機能を使って、オブジェクトの状態を丸ごと出力するメソッドを紹介します。
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 を呼び出して値を取得するわけですが、このとき、「インデックス付きプロパティ」は対象外です。なぜならインデックスとして有効な値が何であるか(整数ならともかく文字列等の場合)全くわからないからです。