180 lines
3.8 KiB
C#
180 lines
3.8 KiB
C#
#region References
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Text;
|
|
#endregion
|
|
|
|
namespace CustomsFramework
|
|
{
|
|
public class ObjectDumper
|
|
{
|
|
private int _level;
|
|
private readonly int _indentSize;
|
|
private readonly StringBuilder _stringBuilder;
|
|
private readonly List<int> _hashListOfFoundElements;
|
|
|
|
private ObjectDumper(int indentSize)
|
|
{
|
|
_indentSize = indentSize;
|
|
_stringBuilder = new StringBuilder();
|
|
_hashListOfFoundElements = new List<int>();
|
|
}
|
|
|
|
public static string Dump(object element)
|
|
{
|
|
return Dump(element, 2);
|
|
}
|
|
|
|
public static string Dump(object element, int indentSize)
|
|
{
|
|
var instance = new ObjectDumper(indentSize);
|
|
return instance.DumpElement(element);
|
|
}
|
|
|
|
private string DumpElement(object element)
|
|
{
|
|
if (element == null || element is ValueType || element is string)
|
|
{
|
|
Write(FormatValue(element));
|
|
}
|
|
else
|
|
{
|
|
var objectType = element.GetType();
|
|
if (!typeof(IEnumerable).IsAssignableFrom(objectType))
|
|
{
|
|
Write("{{{0}}}", objectType.FullName);
|
|
_hashListOfFoundElements.Add(element.GetHashCode());
|
|
_level++;
|
|
}
|
|
|
|
var enumerableElement = element as IEnumerable;
|
|
if (enumerableElement != null)
|
|
{
|
|
foreach (object item in enumerableElement)
|
|
{
|
|
if (item is IEnumerable && !(item is string))
|
|
{
|
|
_level++;
|
|
DumpElement(item);
|
|
_level--;
|
|
}
|
|
else
|
|
{
|
|
if (!AlreadyTouched(item))
|
|
{
|
|
DumpElement(item);
|
|
}
|
|
else
|
|
{
|
|
Write("{{{0}}} <-- bidirectional reference found", item.GetType().FullName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var members = element.GetType().GetMembers(BindingFlags.Public | BindingFlags.Instance);
|
|
foreach (var memberInfo in members)
|
|
{
|
|
var fieldInfo = memberInfo as FieldInfo;
|
|
var propertyInfo = memberInfo as PropertyInfo;
|
|
|
|
if (fieldInfo == null && propertyInfo == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var type = fieldInfo != null ? fieldInfo.FieldType : propertyInfo.PropertyType;
|
|
object value = fieldInfo != null ? fieldInfo.GetValue(element) : propertyInfo.GetValue(element, null);
|
|
|
|
if (type.IsValueType || type == typeof(string))
|
|
{
|
|
Write("{0}: {1}", memberInfo.Name, FormatValue(value));
|
|
}
|
|
else
|
|
{
|
|
var isEnumerable = typeof(IEnumerable).IsAssignableFrom(type);
|
|
Write("{0}: {1}", memberInfo.Name, isEnumerable ? "..." : "{ }");
|
|
|
|
var alreadyTouched = !isEnumerable && AlreadyTouched(value);
|
|
_level++;
|
|
if (!alreadyTouched)
|
|
{
|
|
DumpElement(value);
|
|
}
|
|
else
|
|
{
|
|
Write("{{{0}}} <-- bidirectional reference found", value.GetType().FullName);
|
|
}
|
|
_level--;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!typeof(IEnumerable).IsAssignableFrom(objectType))
|
|
{
|
|
_level--;
|
|
}
|
|
}
|
|
|
|
return _stringBuilder.ToString();
|
|
}
|
|
|
|
private bool AlreadyTouched(object value)
|
|
{
|
|
var hash = value.GetHashCode();
|
|
for (var i = 0; i < _hashListOfFoundElements.Count; i++)
|
|
{
|
|
if (_hashListOfFoundElements[i] == hash)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void Write(string value, params object[] args)
|
|
{
|
|
var space = new string(' ', _level * _indentSize);
|
|
|
|
if (args != null)
|
|
{
|
|
value = string.Format(value, args);
|
|
}
|
|
|
|
_stringBuilder.AppendLine(space + value);
|
|
}
|
|
|
|
private string FormatValue(object o)
|
|
{
|
|
if (o == null)
|
|
{
|
|
return ("null");
|
|
}
|
|
|
|
if (o is DateTime)
|
|
{
|
|
return (((DateTime)o).ToShortDateString());
|
|
}
|
|
|
|
if (o is string)
|
|
{
|
|
return string.Format("\"{0}\"", o);
|
|
}
|
|
|
|
if (o is ValueType)
|
|
{
|
|
return (o.ToString());
|
|
}
|
|
|
|
if (o is IEnumerable)
|
|
{
|
|
return ("...");
|
|
}
|
|
|
|
return ("{ }");
|
|
}
|
|
}
|
|
} |