/// <summary> /// Dumps the contents of the specified value and returns the dumped contents as a string /// </summary> /// <param name="value">The object to dump</param> /// <param name="name">Name to give to the object in the dump; if null the name is just "object"</param> /// <param name="options">A DumpOptions object that defines options for what the dump contains</param> /// <returns>The dumped contents of the object as multi-line string</returns> public static string Dump(object value, string name, DumperOptions options) { using (var writer = new StringWriter(CultureInfo.InvariantCulture)) { Dump(value, name, writer, options); return(writer.ToString()); } }
/// <summary> /// Dumps the specified value to the given TextWriter using the specified name /// </summary> /// <param name="value">The value to dump to the writer</param> /// <param name="name">The name of the value being dumped</param> /// <param name="writer">The TextWriter to dump the value to</param> /// <param name="options">A DumpOptions object that defines options for what the dump contains</param> public static void Dump(object value, string name, TextWriter writer, DumperOptions options) { if (name == null) { name = "object"; } if (writer == null) { throw new ArgumentNullException(nameof(writer)); } if (options == null) { throw new ArgumentNullException(nameof(options)); } var idGenerator = new ObjectIDGenerator(); InternalDump(0, name, value, null, writer, idGenerator, true, options); }
public static void Dump(object obj, string name, DumperOptions options) { Debug(Dumper.Dump(obj, name, options)); }
private static void InternalDump(int indentationLevel, string name, object value, MemberInfo memberInfo, TextWriter writer, ObjectIDGenerator idGenerator, bool recursiveDump, DumperOptions options) { var indentation = new string(' ', indentationLevel *IndentationSpace); //var indentation2 = new string(' ', (indentationLevel + 1) * INDENTATION_SPACE); string accessModifiers = GetAccessModifiers(memberInfo); string declaredTypeName = ImproveTypeName(GetDeclaredTypeName(memberInfo)); string propertyMethods = PropertyMethods(memberInfo); if (value == null) { writer.WriteLine($"{indentation}{accessModifiers}{declaredTypeName}{name}{propertyMethods} = <null>"); return; } Type type = value.GetType(); // figure out if this is an object that has already been dumped, or is currently being dumped string keyRef = string.Empty; string keyPrefix = string.Empty; if (!type.IsValueType) { bool firstTime; long key = idGenerator.GetId(value, out firstTime); if (!firstTime) { keyRef = string.Format(CultureInfo.InvariantCulture, " (see #{0})", key); } else { keyPrefix = string.Format(CultureInfo.InvariantCulture, "#{0}: ", key); } } // work out how a simple dump of the value should be done bool isString = value is string; string typeName = ImproveTypeName(value.GetType().ToString()); typeName = (declaredTypeName.TrimEnd() == typeName) ? "" : " [" + typeName + "]"; string formattedValue = value.ToString(); var exception = value as Exception; if (exception != null) { formattedValue = exception.GetType().Name + ": " + exception.Message; } if (value is bool) { formattedValue = value.ToString().ToLowerInvariant(); } // escape tabs and line feeds formattedValue = formattedValue.Replace("\t", "\\t").Replace("\n", "\\n").Replace("\r", "\\r"); // Give non-numerical values double-quotes if (!(value is int || value is float || value is byte || value is decimal || value is double || value is long || value is short || value is uint || value is ulong || value is ushort || value is bool || value is sbyte)) { formattedValue = "\"" + formattedValue + "\""; } // chop at MAX_CONTENT characters int length = formattedValue.Length; if (length > MaxValueLength) { formattedValue = formattedValue.Substring(0, MaxValueLength); } if (length > MaxValueLength) { formattedValue += " (+" + (length - MaxValueLength) + " chars)"; } var collection = value as ICollection; if (collection != null) { formattedValue += " (Count = " + collection.Count + ")"; } //writer.WriteLine($"{indentation}{keyPrefix}{accessModifiers}{declaredTypeName}{name}{propertyMethods} = {formattedValue}{typeName}{keyRef}"); // Removed keyref info for now, because it makes output cluttered; let's think about some better visualization later writer.WriteLine($"{indentation}{accessModifiers}{declaredTypeName}{name}{propertyMethods} = {formattedValue}{typeName}"); // Avoid dumping objects we've already dumped, or is already in the process of dumping if (keyRef.Length > 0) { return; } // don't dump strings, we already got at around 80 characters of those dumped if (isString) { return; } // don't dump value-types in the System namespace if (type.IsValueType && type.FullName == "System." + type.Name) { return; } // Avoid certain types that will result in endless recursion if (type.FullName == "System.Reflection." + type.Name) { return; } // Avoid types that are excluded in options if (options.DontDumpTypes.Contains(type)) { return; } if (value is System.Security.Principal.SecurityIdentifier) { return; } if (!recursiveDump) { return; } if (indentationLevel / 2 > options.MaxDepth - 1) { return; } // Iterate enumerable var enumerable = value as IEnumerable; if (enumerable != null) { if (options.IterateEnumerable) { int i = 0; foreach (var element in enumerable) { InternalDump(indentationLevel + 2, "[" + i + "]", element, null, writer, idGenerator, true, options); i++; if (i >= 100) { writer.WriteLine(new string(' ', (indentationLevel + 2) * IndentationSpace) + "[ enumeration truncated ]"); break; } } } // Don't show members of enumerable if option is set if (!options.WithEnumerableMembers) { return; } } var bfPublic = options.WithNonPublic ? BindingFlags.NonPublic : BindingFlags.Default; var bfStatic = options.WithStatic ? BindingFlags.Static : BindingFlags.Default; PropertyInfo[] properties = (from property in type.GetProperties(BindingFlags.Instance | bfStatic | BindingFlags.Public | bfPublic) where property.GetIndexParameters().Length == 0 && property.CanRead select property).ToArray(); IEnumerable <FieldInfo> fields = !options.WithFields ? Enumerable.Empty <FieldInfo>() : type.GetFields(BindingFlags.Instance | bfStatic | BindingFlags.Public | bfPublic); if (!properties.Any() && !fields.Any()) { return; } writer.WriteLine($"{indentation}{{"); if (fields.Any()) { //writer.WriteLine($"{indentation2}fields {{"); foreach (FieldInfo field in fields) { try { object fieldValue = field.GetValue(value); InternalDump(indentationLevel + 2, field.Name, fieldValue, field, writer, idGenerator, true, options); } catch (TargetInvocationException ex) { InternalDump(indentationLevel + 2, field.Name, ex, field, writer, idGenerator, false, options); } } //writer.WriteLine($"{indentation2}}}"); } if (properties.Any()) { //writer.WriteLine($"{indentation2}properties {{"); foreach (PropertyInfo property in properties) { try { object propertyValue = property.GetValue(value, null); InternalDump(indentationLevel + 2, property.Name, propertyValue, property, writer, idGenerator, true, options); } catch (TargetInvocationException ex) { InternalDump(indentationLevel + 2, property.Name, ex.InnerException, property, writer, idGenerator, false, options); } catch (Exception ex) { InternalDump(indentationLevel + 2, property.Name, ex, property, writer, idGenerator, false, options); } } //writer.WriteLine($"{indentation2}}}"); } writer.WriteLine($"{indentation}}}"); }
/// <summary> /// Dumps the contents of the specified value and returns the dumped contents as a string. /// </summary> /// <param name="value">The object to dump</param> /// <param name="options">A DumpOptions object that defines options for what the dump contains</param> /// <returns>The dumped contents of the object as multi-line string</returns> public static string Dump(object value, DumperOptions options) { return(Dump(value, null, options)); }