/// Mark type as bloat. For bloattypes its ToString() output is copied to the dump instead of the proper tree walking. public static void AddBloatType(Type exc) { lock (s_bloatTypes) s_bloatTypes[exc.FullName] = true; } /// Mark type as bloat . For bloat types its ToString() output is copied to the dump instead of the proper tree walking. public static void AddBloatType(string typename) { lock (s_bloatTypes) s_bloatTypes.Add(typename, true); } /// Mark a property as bloat . For bloat properties its ToString() output is copied to the dump instead of the proper tree walking. public static void AddBloatProperty(Type type, string propertyName) { addProperty(type, propertyName, false); } /// Mark a property as one that should not be dumped, for example if the property has a side effect. public static void AddHiddenProperty(Type type, string propertyName) { addProperty(type, propertyName, true); } /// Add a friendly type name public static void AddTypeName(Type type, string typename) { lock (s_bloatTypes) s_friendlyName.Add(type, typename); } #endregion #region --- Implementation details --- private readonly object _object; private readonly Type _type; private readonly string _name; private readonly int _level; private readonly DumpSettings _settings; private static readonly DumpSettings s_defaultSettings = new DumpSettings(); private static void addProperty(Type type, string propertyName, bool sideEffect) { lock (s_propertyHints) { Dictionary<string, bool> prop; if (!s_propertyHints.TryGetValue(type, out prop)) { prop = new Dictionary<string, bool>(); s_propertyHints.Add(type, prop); } prop[propertyName] = sideEffect; } } private class ReferenceComparer : IEqualityComparer<object> { bool IEqualityComparer<object>.Equals(object x, object y) { return ReferenceEquals(x, y); } int IEqualityComparer<object>.GetHashCode(object obj) { if (ReferenceEquals(obj, null)) return 0; return obj.GetHashCode(); } } private static readonly Dictionary<string, bool> s_bloatTypes = initBloatTypes(); // For each special store true=side effect, false=bloat private static readonly Dictionary<Type, Dictionary<string, bool>> s_propertyHints = initPropertyHints(); private static readonly Dictionary<Type, string> s_friendlyName = initFriendlyNames(); private static Dictionary<string, bool> initBloatTypes() { Dictionary<string, bool> ret = new Dictionary<string, bool>(); foreach (string s in new string[] {"System.DateTime", "System.Type", "System.Guid", "System.Security.Principal.SecurityIdentifier", "System.Xml.Linq.XElement", "System.Xml.Linq.XDocument", "System.Reflection.RuntimeConstructorInfo", "System.Reflection.RuntimePropertyInfo", "System.Reflection.RuntimeMethodInfo", "System.RuntimeType", "System.Reflection.MethodBase", "System.Security.Policy.Evidence", "System.Globalization.CultureInfo", "System.Version"}) ret[s] = true; return ret; } private static Dictionary<Type, string> initFriendlyNames() { Dictionary<Type, string> r = new Dictionary<Type, string>(); r.Add(typeof(string), "string"); r.Add(typeof(int), "int"); r.Add(typeof(uint), "uint"); r.Add(typeof(long), "long"); r.Add(typeof(ulong), "ulong"); r.Add(typeof(short), "short"); r.Add(typeof(ushort), "ushort"); r.Add(typeof(char), "char"); r.Add(typeof(byte), "byte"); r.Add(typeof(decimal), "decimal"); r.Add(typeof(object), "object"); r.Add(typeof(void), "void"); r.Add(typeof(bool), "bool"); return r; } private static Dictionary<Type, Dictionary<string, bool>> initPropertyHints() { Dictionary<Type, Dictionary<string, bool>> s = new Dictionary<Type, Dictionary<string, bool>>(); // FileSystemInfo Parent and Root properties are examples of "bloat" properties and should be dumped with ToString Dictionary<string, bool> data = new Dictionary<string, bool>(); data["Parent"] = false; data["Root"] = false; s[typeof(FileSystemInfo)] = data; return s; } private readonly StringBuilder _out = new StringBuilder(10000); readonly Dictionary<object, int> _usedMap = new Dictionary<object, int>(new ReferenceComparer()); private int _counter = 0; void process2(string name, Type t, object o, int depth) { string typeName = GetFriendlyTypeName(t, _settings.UseFullClassNames); _out.Append(new string(' ', depth * 2)); if (!string.IsNullOrEmpty(name)) _out.Append(name + " = "); _out.Append("(" + typeName + ") "); if (o == null) { _out.Append("<null>"); return; } if ((t != typeof(string) && (o is string)) || _settings.MaxDepth <= depth) { _out.Append("\"" + toEscapedString(o) + "\" /* ToString */"); return; } if (t == typeof(string) || (o is string)) { _out.Append("\"" + toEscapedString(o) + "\""); return; } BindingFlags flags = BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public; if (_settings.DisplayPrivate) flags |= BindingFlags.NonPublic; PropertyInfo[] props = t.GetProperties(flags | BindingFlags.GetProperty); FieldInfo[] fi = t.GetFields(flags); if (t.IsPrimitive || t.IsEnum || (t.IsValueType && props.Length == 0 && fi.Length == 0) || s_bloatTypes.ContainsKey(t.FullName) || t == typeof(decimal)) { if ((t == typeof(int)) || (t == typeof(byte)) || (t == typeof(uint)) || t == typeof(long) || t == typeof(ulong)) _out.AppendFormat(" {0} (0x{0:x})", o); else if (t == typeof(DateTime) || t == typeof(DateTime?)) _out.AppendFormat("{0:o} ({1})", o, ((DateTime)o).Kind); else if (t == typeof(Guid)) _out.Append(((Guid)o).ToString("B")); else if (t == typeof(bool)) _out.Append(((bool)o) ? "true" : "false"); else if (t.IsEnum) _out.AppendFormat("[{0}] /* 0x{0:x} */", o); else _out.AppendFormat("[{0}]", toEscapedString(o)); return; } if (t.IsClass) { int id; if (_usedMap.TryGetValue(o, out id)) { _out.AppendFormat("<see #{0}, {1:x8} above>", id, o.GetHashCode()); return; } _usedMap[o] = ++_counter; } if (t.IsArray && ((Array)o).Rank == 1) { Array arr; if (t.IsArray) arr = (o as Array); else arr = t.GetMethod("ToArray").Invoke(o, new object[] { }) as Array; processArray(t, arr, depth); } else if (o as IEnumerable != null) { writeBrace(o); processEnumerables(o as IEnumerable, depth); } else { writeBrace(o); Dictionary<string, bool> propNames = null; foreach (KeyValuePair<Type, Dictionary<string, bool>> info in s_propertyHints) if (t == info.Key || t.IsSubclassOf(info.Key)) { propNames = info.Value; break; } foreach (PropertyInfo p in props) { bool sideEffect; if (propNames != null && propNames.TryGetValue(p.Name, out sideEffect)) { if (!sideEffect) dumpProperty(p, o, depth + 1, true); else { process2(p.Name, p.PropertyType, "<ignored>", depth + 1); _out.AppendLine(); } } else dumpProperty(p, o, depth + 1, false); } foreach (FieldInfo f in fi) dumpField(f, o, depth + 1); } _out.Append(new string(' ', depth * 2)); _out.Append("}"); } private static string toEscapedString(object o) { return o.ToString().Replace("\r", "\\r").Replace("\n", "\\n").Replace("\b", "\\b").Replace("\t", "\\t"); } private void writeBrace(object o) { _out.AppendFormat("{{ /* #{0}, {1:x8} */ ", _counter, o.GetHashCode()); _out.AppendLine(); } private void dumpProperty(PropertyInfo p, object o, int level, bool asString) { try { if (p.GetIndexParameters().Length == 0) { object obj = p.GetValue(o, null); if (asString) process2(p.Name, (obj == null) ? p.PropertyType : obj.GetType(), (obj ?? "<null>").ToString(), level); else process2(p.Name, (obj == null) ? p.PropertyType : obj.GetType(), obj, level); _out.AppendLine(); } else { _out.Append(new string(' ', level * 2)); _out.AppendLine(p.Name + " = " + "(" + GetFriendlyTypeName(p.PropertyType) + ") ??? indexed property ignored"); } } catch (Exception e) { _out.Append(new string(' ', level * 2)); _out.AppendLine(p.Name + " = " + "??? thrown " + e.GetType().FullName); } } private void dumpField(FieldInfo p, object o, int level) { try { object obj = p.GetValue(o); process2(p.Name, (obj == null) ? p.FieldType : obj.GetType(), obj, level); _out.AppendLine(); } catch (Exception e) { _out.Append(new string(' ', level * 2)); _out.AppendLine(p.Name + " = " + "??? thrown " + e.GetType().FullName); } } private void processEnumerables(IEnumerable enumerable, int nLevel) { int index = 0; foreach (object info in enumerable) { Type tinside = info == null ? typeof(object) : info.GetType(); process2("[" + index + "]", tinside, info, nLevel + 1); _out.AppendLine(); if (index++ > _settings.MaxItems) { _out.AppendLine("..."); break; } } } private void processArray(Type t, Array arr, int nLevel) { Type tInside = t.GetElementType(); if (tInside == typeof(byte)) { byte[] b = (byte[])arr; _out.AppendFormat(" {0} bytes [ ", arr.Length); for (int i = 0; i < b.Length; ++i) { if (i != 0) _out.Append(' '); _out.Append(b[i].ToString("x2")); if (i > _settings.MaxItems) { _out.Append("..."); break; } } _out.Append(" ]"); return; } _out.Append(" array[" + arr.Length + "] "); writeBrace(arr); processEnumerables(arr, nLevel); } #endregion }}Lines 501–849 of 849. Previous lines
/// Mark type as bloat. For bloattypes its ToString() output is copied to the dump instead of the proper tree walking. public static void AddBloatType(Type exc) { lock (s_bloatTypes) s_bloatTypes[exc.FullName] = true; } /// Mark type as bloat . For bloat types its ToString() output is copied to the dump instead of the proper tree walking. public static void AddBloatType(string typename) { lock (s_bloatTypes) s_bloatTypes.Add(typename, true); } /// Mark a property as bloat . For bloat properties its ToString() output is copied to the dump instead of the proper tree walking. public static void AddBloatProperty(Type type, string propertyName) { addProperty(type, propertyName, false); } /// Mark a property as one that should not be dumped, for example if the property has a side effect. public static void AddHiddenProperty(Type type, string propertyName) { addProperty(type, propertyName, true); } /// Add a friendly type name public static void AddTypeName(Type type, string typename) { lock (s_bloatTypes) s_friendlyName.Add(type, typename); } #endregion #region --- Implementation details --- private readonly object _object; private readonly Type _type; private readonly string _name; private readonly int _level; private readonly DumpSettings _settings; private static readonly DumpSettings s_defaultSettings = new DumpSettings(); private static void addProperty(Type type, string propertyName, bool sideEffect) { lock (s_propertyHints) { Dictionary<string, bool> prop; if (!s_propertyHints.TryGetValue(type, out prop)) { prop = new Dictionary<string, bool>(); s_propertyHints.Add(type, prop); } prop[propertyName] = sideEffect; } } private class ReferenceComparer : IEqualityComparer<object> { bool IEqualityComparer<object>.Equals(object x, object y) { return ReferenceEquals(x, y); } int IEqualityComparer<object>.GetHashCode(object obj) { if (ReferenceEquals(obj, null)) return 0; return obj.GetHashCode(); } } private static readonly Dictionary<string, bool> s_bloatTypes = initBloatTypes(); // For each special store true=side effect, false=bloat private static readonly Dictionary<Type, Dictionary<string, bool>> s_propertyHints = initPropertyHints(); private static readonly Dictionary<Type, string> s_friendlyName = initFriendlyNames(); private static Dictionary<string, bool> initBloatTypes() { Dictionary<string, bool> ret = new Dictionary<string, bool>(); foreach (string s in new string[] {"System.DateTime", "System.Type", "System.Guid", "System.Security.Principal.SecurityIdentifier", "System.Xml.Linq.XElement", "System.Xml.Linq.XDocument", "System.Reflection.RuntimeConstructorInfo", "System.Reflection.RuntimePropertyInfo", "System.Reflection.RuntimeMethodInfo", "System.RuntimeType", "System.Reflection.MethodBase", "System.Security.Policy.Evidence", "System.Globalization.CultureInfo", "System.Version"}) ret[s] = true; return ret; } private static Dictionary<Type, string> initFriendlyNames() { Dictionary<Type, string> r = new Dictionary<Type, string>(); r.Add(typeof(string), "string"); r.Add(typeof(int), "int"); r.Add(typeof(uint), "uint"); r.Add(typeof(long), "long"); r.Add(typeof(ulong), "ulong"); r.Add(typeof(short), "short"); r.Add(typeof(ushort), "ushort"); r.Add(typeof(char), "char"); r.Add(typeof(byte), "byte"); r.Add(typeof(decimal), "decimal"); r.Add(typeof(object), "object"); r.Add(typeof(void), "void"); r.Add(typeof(bool), "bool"); return r; } private static Dictionary<Type, Dictionary<string, bool>> initPropertyHints() { Dictionary<Type, Dictionary<string, bool>> s = new Dictionary<Type, Dictionary<string, bool>>(); // FileSystemInfo Parent and Root properties are examples of "bloat" properties and should be dumped with ToString Dictionary<string, bool> data = new Dictionary<string, bool>(); data["Parent"] = false; data["Root"] = false; s[typeof(FileSystemInfo)] = data; return s; } private readonly StringBuilder _out = new StringBuilder(10000); readonly Dictionary<object, int> _usedMap = new Dictionary<object, int>(new ReferenceComparer()); private int _counter = 0; void process2(string name, Type t, object o, int depth) { string typeName = GetFriendlyTypeName(t, _settings.UseFullClassNames); _out.Append(new string(' ', depth * 2)); if (!string.IsNullOrEmpty(name)) _out.Append(name + " = "); _out.Append("(" + typeName + ") "); if (o == null) { _out.Append("<null>"); return; } if ((t != typeof(string) && (o is string)) || _settings.MaxDepth <= depth) { _out.Append("\"" + toEscapedString(o) + "\" /* ToString */"); return; } if (t == typeof(string) || (o is string)) { _out.Append("\"" + toEscapedString(o) + "\""); return; } BindingFlags flags = BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.Public; if (_settings.DisplayPrivate) flags |= BindingFlags.NonPublic; PropertyInfo[] props = t.GetProperties(flags | BindingFlags.GetProperty); FieldInfo[] fi = t.GetFields(flags); if (t.IsPrimitive || t.IsEnum || (t.IsValueType && props.Length == 0 && fi.Length == 0) || s_bloatTypes.ContainsKey(t.FullName) || t == typeof(decimal)) { if ((t == typeof(int)) || (t == typeof(byte)) || (t == typeof(uint)) || t == typeof(long) || t == typeof(ulong)) _out.AppendFormat(" {0} (0x{0:x})", o); else if (t == typeof(DateTime) || t == typeof(DateTime?)) _out.AppendFormat("{0:o} ({1})", o, ((DateTime)o).Kind); else if (t == typeof(Guid)) _out.Append(((Guid)o).ToString("B")); else if (t == typeof(bool)) _out.Append(((bool)o) ? "true" : "false"); else if (t.IsEnum) _out.AppendFormat("[{0}] /* 0x{0:x} */", o); else _out.AppendFormat("[{0}]", toEscapedString(o)); return; } if (t.IsClass) { int id; if (_usedMap.TryGetValue(o, out id)) { _out.AppendFormat("<see #{0}, {1:x8} above>", id, o.GetHashCode()); return; } _usedMap[o] = ++_counter; } if (t.IsArray && ((Array)o).Rank == 1) { Array arr; if (t.IsArray) arr = (o as Array); else arr = t.GetMethod("ToArray").Invoke(o, new object[] { }) as Array; processArray(t, arr, depth); } else if (o as IEnumerable != null) { writeBrace(o); processEnumerables(o as IEnumerable, depth); } else { writeBrace(o); Dictionary<string, bool> propNames = null; foreach (KeyValuePair<Type, Dictionary<string, bool>> info in s_propertyHints) if (t == info.Key || t.IsSubclassOf(info.Key)) { propNames = info.Value; break; } foreach (PropertyInfo p in props) { bool sideEffect; if (propNames != null && propNames.TryGetValue(p.Name, out sideEffect)) { if (!sideEffect) dumpProperty(p, o, depth + 1, true); else { process2(p.Name, p.PropertyType, "<ignored>", depth + 1); _out.AppendLine(); } } else dumpProperty(p, o, depth + 1, false); } foreach (FieldInfo f in fi) dumpField(f, o, depth + 1); } _out.Append(new string(' ', depth * 2)); _out.Append("}"); } private static string toEscapedString(object o) { return o.ToString().Replace("\r", "\\r").Replace("\n", "\\n").Replace("\b", "\\b").Replace("\t", "\\t"); } private void writeBrace(object o) { _out.AppendFormat("{{ /* #{0}, {1:x8} */ ", _counter, o.GetHashCode()); _out.AppendLine(); } private void dumpProperty(PropertyInfo p, object o, int level, bool asString) { try { if (p.GetIndexParameters().Length == 0) { object obj = p.GetValue(o, null); if (asString) process2(p.Name, (obj == null) ? p.PropertyType : obj.GetType(), (obj ?? "<null>").ToString(), level); else process2(p.Name, (obj == null) ? p.PropertyType : obj.GetType(), obj, level); _out.AppendLine(); } else { _out.Append(new string(' ', level * 2)); _out.AppendLine(p.Name + " = " + "(" + GetFriendlyTypeName(p.PropertyType) + ") ??? indexed property ignored"); } } catch (Exception e) { _out.Append(new string(' ', level * 2)); _out.AppendLine(p.Name + " = " + "??? thrown " + e.GetType().FullName); } } private void dumpField(FieldInfo p, object o, int level) { try { object obj = p.GetValue(o); process2(p.Name, (obj == null) ? p.FieldType : obj.GetType(), obj, level); _out.AppendLine(); } catch (Exception e) { _out.Append(new string(' ', level * 2)); _out.AppendLine(p.Name + " = " + "??? thrown " + e.GetType().FullName); } } private void processEnumerables(IEnumerable enumerable, int nLevel) { int index = 0; foreach (object info in enumerable) { Type tinside = info == null ? typeof(object) : info.GetType(); process2("[" + index + "]", tinside, info, nLevel + 1); _out.AppendLine(); if (index++ > _settings.MaxItems) { _out.AppendLine("..."); break; } } } private void processArray(Type t, Array arr, int nLevel) { Type tInside = t.GetElementType(); if (tInside == typeof(byte)) { byte[] b = (byte[])arr; _out.AppendFormat(" {0} bytes [ ", arr.Length); for (int i = 0; i < b.Length; ++i) { if (i != 0) _out.Append(' '); _out.Append(b[i].ToString("x2")); if (i > _settings.MaxItems) { _out.Append("..."); break; } } _out.Append(" ]"); return; } _out.Append(" array[" + arr.Length + "] "); writeBrace(arr); processEnumerables(arr, nLevel); } #endregion } }