遍历打印对象的所有变量和属性¶
C# Dumper遍历打印对象的所有变量和属性及变量和属性的值。将C#中字段和属性(包含set和get访问器的)都遍历出来及对应结构序列化成字符串 在对象调试的时候想查看一个对象的所有变量值包括公有变量和私有变量,都想打印出来,这里就提供将对象所有变量和值都序列化成字符串;使用C#的反射功能打印所有变量,建议在调试时可以使用,发布时屏蔽调用,增加效率。 例如protobuf和json对象等数据对象为了验证及查看数据,这样是很方便的
using System;
using System.Collections;
using System.Reflection;
using System.Text;
using UnityEngine;
namespace Codingriver
{
public static class Dumper
{
private static readonly StringBuilder _text = new StringBuilder("", 1024);
private static void AppendIndent(int num)
{
_text.Append(' ', num);
}
/// <summary>
///
/// </summary>
/// <param name="obj"></param>
/// <param name="depth">防止 stack overflow</param>
/// <param name="showField">是否遍历字段</param>
private static void DoDump(object obj, int depth=100, bool showField = true)
{
if (obj == null)
{
_text.Append("null");
_text.Append(",");
return;
}
if(depth ==0)
{
_text.Append("DEPTH_NULL,");
return;
}
Type t = obj.GetType();
//repeat field
if (obj is IList)
{
/*
_text.Append(t.FullName);
_text.Append(",");
AppendIndent(1);
*/
_text.Append("[");
IList list = obj as IList;
foreach (object v in list)
{
DoDump(v,depth, showField);
}
_text.Append("]");
}
else if (t.IsValueType)
{
_text.Append(obj);
_text.Append(",");
AppendIndent(1);
}
else if (obj is string)
{
_text.Append("\"");
_text.Append(obj);
_text.Append("\"");
_text.Append(",");
AppendIndent(1);
}
else if (obj is byte[])
{
_text.Append("\"");
_text.Append(Encoding.UTF8.GetString((byte[])obj));
_text.Append("\"");
_text.Append(",");
AppendIndent(1);
}
else if (t.IsArray)
{
Array a = (Array)obj;
_text.Append("[");
for (int i = 0; i < a.Length; i++)
{
_text.Append(i);
_text.Append(":");
DoDump(a.GetValue(i), depth, showField);
}
_text.Append("]");
}
else if (t.IsClass)
{
_text.Append($"<{t.Name}>");
_text.Append("{");
var props = t.GetProperties(BindingFlags.Public | BindingFlags.NonPublic |BindingFlags.Instance);
if (props.Length > 0)
{
foreach (PropertyInfo info in props)
{
_text.Append(info.Name);
_text.Append(":");
object value = info.GetGetMethod().Invoke(obj, null);
DoDump(value, depth-1, showField);
}
}
var fields = t.GetFields(BindingFlags.Public |BindingFlags.NonPublic | BindingFlags.Instance);
if (showField&&fields.Length > 0)
{
foreach (FieldInfo info in fields)
{
_text.Append(info.Name);
_text.Append(":");
object value = info.GetValue(obj);
DoDump(value, depth-1, showField);
}
}
_text.Append("}");
}
else
{
Debug.LogWarning("unsupport type: " + t.FullName);
_text.Append(obj);
_text.Append(",");
AppendIndent(1);
}
}
public static string DumpAsString(object obj, int depth = 100, bool showField = true, string hint = "")
{
_text.Clear();
_text.Append(hint);
DoDump(obj,depth, showField);
return _text.ToString();
}
}
}
测试案例:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Codingriver;
public class DumperTest : MonoBehaviour
{
public class InnerTest
{
public class InnerA
{
string innerStr = "my name is codingriver";
int key = 99;
}
int a = 1;
string b = "hello";
int[] arr = new int[] { 5, 76, 12 };
InnerA innerA = new InnerA();
}
public class TestData
{
public string[] Sitekeys = new string[] { "https://codingriver.github.io", "codingriver" };
string name = "codingriver";
int m_Main = 1024;
protected InnerTest obj = new InnerTest();
public int Main
{
get
{
return m_Main;
}
set
{
m_Main = value;
}
}
}
// Start is called before the first frame update
void Start()
{
TestData data = new TestData();
Debug.Log(Dumper.DumpAsString(data));
}
}
测试结果:
<TestData>{Main:1024, Sitekeys:["https://codingriver.github.io", "codingriver", ]name:"codingriver", m_Main:1024, obj:<InnerTest>{a:1, b:"hello", arr:[5, 76, 12, ]innerA:<InnerA>{innerStr:"my name is codingriver", key:99, }}}
UnityEngine.Debug:Log(Object)
DumperTest:Start() (at Assets/DumperTest.cs:48)