VB.NET 入れ子のオブジェクトの内部構造を文字列に変換する方法 サンプルコード

このまま動作します。
コピぺ、編集して利用するためのコードの断片です。
自由に使って頂いて構いません。

例えば、以下の構造のオブジェクトを、以下のような文字列に変換できます。
オブジェクトの入れ子に対応しています。
デバッカが使えない環境で、オブジェクトの内容をログに出力して、デバック等に使用できます。

```vb:オブジェクトの構造.vb
Imports System.Collections.Generic

Class class_1
    Public a As List(Of String)
    Public b As Integer = 0
    Public c As class_1
End Class
```

```:変換した文字列
▽(汎用.Form_Test+class_1)-------------------------------
|       ▽a(System.Collections.Generic.List`1[System.String])-------------------------------
|       |       ▽_items(System.String[])-------------------------------
|       |       |
|       |       △_items(System.String[])-------------------------------
|       |       
|       |       _size(System.Int32) = 3
|       |       _version(System.Int32) = 0
|       |       _syncRoot = Nothing
|       |       a[0](System.String) = "aaa"
|       |       a[1](System.String) = "bbb"
|       |       a[2](System.String) = "ccc"
|       |
|       △a(System.Collections.Generic.List`1[System.String])-------------------------------
|       
|       b(System.Int32) = 1
|       ▽c(汎用.Form_Test+class_1)-------------------------------
|       |       ▽a(System.Collections.Generic.List`1[System.String])-------------------------------
|       |       |       ▽_items(System.String[])-------------------------------
|       |       |       |
|       |       |       △_items(System.String[])-------------------------------
|       |       |       
|       |       |       _size(System.Int32) = 3
|       |       |       _version(System.Int32) = 0
|       |       |       _syncRoot = Nothing
|       |       |       a[0](System.String) = "ddd"
|       |       |       a[1](System.String) = "eee"
|       |       |       a[2](System.String) = "fff"
|       |       |
|       |       △a(System.Collections.Generic.List`1[System.String])-------------------------------
|       |       
|       |       b(System.Int32) = 2
|       |       c = Nothing
|       |
|       △c(汎用.Form_Test+class_1)-------------------------------
|       
|
△(汎用.Form_Test+class_1)-------------------------------
```

以下コードでオブジェクトを文字列に変換できます。

```vb:呼び出しかた.vb
s = ObjectToString(o)
```
以下の共通処理を任意の場所に貼り付ければそのまま使用できます。

```vb:共通処理.vb
Imports System.Text
Imports System.Reflection

''' <summary>
''' 指定されたオブジェクトの内部構造の文字列表現を返却する。
''' </summary>
''' <param name="targetObject">内部構造の文字列表現を取得する対象のオブジェクト</param>
''' <returns>オブジェクトの内部構造の文字列表現</returns>
''' <remarks></remarks>
Public Function ObjectToString(targetObject As Object) As String
    Dim buf As New StringBuilder
    _ObjectToString("", targetObject, buf, "")
    Return buf.ToString
End Function

''' <summary>
''' 指定されたオブジェクトの内部構造の文字列表現を返却する。
''' 内部呼び出し用なので、ObjectToString以外から直接呼び出さない。
''' </summary>
''' <param name="targetFieldName">解析するオブジェクトのフィールド名</param>
''' <param name="targetObject">内部構造の文字列表現を取得する対象のオブジェクト</param>
''' <param name="buf">オブジェクトの内部構造の文字列表現の出力先バッファ</param>
''' <param name="nestString">オブジェクトの入れ子構造を表現するための文字列</param>
''' <remarks></remarks>
Private Sub _ObjectToString(targetFieldName As String, targetObject As Object, buf As StringBuilder, nestString As String)
    Try
        If nestString.Length >= 200 Then 'ここの数を調整して解析を打ち切る階層の深さを変更しても良い。
            Throw New Exception("階層が深すぎるので解析を打ち切ります")
        End If

        If targetObject Is Nothing Then
            buf.AppendLine(nestString + targetFieldName + " = Nothing")

        ElseIf targetObject.GetType Is GetType(Byte) OrElse
            targetObject.GetType Is GetType(Short) OrElse
            targetObject.GetType Is GetType(Integer) OrElse
            targetObject.GetType Is GetType(Long) OrElse
            targetObject.GetType Is GetType(Single) OrElse
            targetObject.GetType Is GetType(Double) OrElse
            targetObject.GetType Is GetType(Decimal) OrElse
            targetObject.GetType Is GetType(Char) OrElse
            targetObject.GetType Is GetType(Boolean) OrElse
            targetObject.GetType Is GetType(Date) Then
            buf.AppendLine(nestString + targetFieldName + "(" + targetObject.GetType.ToString + ") = " + targetObject.ToString)

        ElseIf targetObject.GetType Is GetType(String) Then
            buf.AppendLine(nestString + targetFieldName + "(" + targetObject.GetType.ToString + ") = """ + targetObject.ToString.Replace(vbCrLf, vbCrLf + nestString + "    ") + """")

        ElseIf targetObject.GetType.ToString.IndexOf("System.Collections.Generic.List") = 0 Then
            '型：List(Of ...) への対応
            'リストの内部情報はType.GetFieldsから解析する事ができないので、自前で解析する。
            buf.AppendLine(nestString + "▽" + targetFieldName + "(" + targetObject.GetType.ToString + ")-------------------------------")
            For Each field In targetObject.GetType.GetFields(BindingFlags.FlattenHierarchy Or BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance)
                _ObjectToString(field.Name, field.GetValue(targetObject), buf, nestString + "|       ")
            Next

            Dim count = DirectCast(targetObject.GetType.GetProperty("Count").GetValue(targetObject, Nothing), Integer)
            For index = 0 To count - 1
                Dim param As Object() = {index}
                Dim item As Object = targetObject.GetType.GetProperty("Item").GetValue(targetObject, param)
                _ObjectToString(targetFieldName + "[" + index.ToString + "]", item, buf, nestString + "|       ")
            Next

            buf.AppendLine(nestString + "|")
            buf.AppendLine(nestString + "△" + targetFieldName + "(" + targetObject.GetType.ToString + ")-------------------------------")
            buf.AppendLine(nestString)

        Else
            buf.AppendLine(nestString + "▽" + targetFieldName + "(" + targetObject.GetType.ToString + ")-------------------------------")
            For Each field In targetObject.GetType.GetFields(BindingFlags.FlattenHierarchy Or BindingFlags.Public Or BindingFlags.NonPublic Or BindingFlags.Instance)
                _ObjectToString(field.Name, field.GetValue(targetObject), buf, nestString + "|       ")
            Next

            buf.AppendLine(nestString + "|")
            buf.AppendLine(nestString + "△" + targetFieldName + "(" + targetObject.GetType.ToString + ")-------------------------------")
            buf.AppendLine(nestString)
        End If

    Catch ex As Exception
        buf.AppendLine(nestString + "▽" + "(解析中に例外発生)-------------------------------")
        buf.AppendLine(nestString + "|       " + ex.ToString)
        buf.AppendLine(nestString + "|       " + ex.Message)
        buf.AppendLine(nestString + "|       " + ex.StackTrace)
        buf.AppendLine(nestString + "|")
        buf.AppendLine(nestString + "△" + "(解析中に例外発生)-------------------------------")
    End Try
End Sub
```