オブジェクトのダンプ

今回は、プロパティを列挙する機能と、その値を取得する機能を使って、オブジェクトの状態を丸ごと出力するメソッドを紹介します。

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 を呼び出して値を取得するわけですが、このとき、「インデックス付きプロパティ」は対象外です。なぜならインデックスとして有効な値が何であるか(整数ならともかく文字列等の場合)全くわからないからです。