Skip to content

Instantly share code, notes, and snippets.

@hadoan
Last active January 2, 2016 18:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hadoan/8347054 to your computer and use it in GitHub Desktop.
Save hadoan/8347054 to your computer and use it in GitHub Desktop.
Dumper
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Runtime.Remoting;
namespace ObjectDumper
{
/// <summary>
/// This class implements the core dumping algorithm.
/// </summary>
public static class Dumper
{
public static string Dump(object value)
{
using (var textWriter = new StringWriter())
{
Dump(value, "name", textWriter);
return textWriter.ToString();
}
}
/// <summary>
/// Dumps the specified value to the <see cref="TextWriter"/> using the
/// specified <paramref name="name"/>.
/// </summary>
/// <param name="value">
/// The value to dump to the <paramref name="writer"/>.
/// </param>
/// <param name="name">
/// The name of the <paramref name="value"/> being dumped.
/// </param>
/// <param name="writer">
/// The <see cref="TextWriter"/> to dump the <paramref name="value"/> to.
/// </param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="name"/> is <c>null</c> or empty.</para>
/// <para>- or -</para>
/// <para><paramref name="writer"/> is <c>null</c>.</para>
/// </exception>
public static void Dump(object value, string name, TextWriter writer)
{
if (StringEx.IsNullOrWhiteSpace(name))
throw new ArgumentNullException("name");
if (writer == null)
throw new ArgumentNullException("writer");
var idGenerator = new ObjectIDGenerator();
InternalDump(0, name, value, writer, idGenerator, true);
}
private static void InternalDump(int indentationLevel, string name, object value, TextWriter writer, ObjectIDGenerator idGenerator, bool recursiveDump)
{
var indentation = new string(' ', indentationLevel * 3);
if (value == null)
{
writer.WriteLine("{0}{1} = <null>", indentation, name);
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 = value.GetType().FullName;
string formattedValue = value.ToString();
var exception = value as Exception;
if (exception != null)
{
formattedValue = exception.GetType().Name + ": " + exception.Message;
}
if (formattedValue == typeName)
formattedValue = string.Empty;
else
{
// escape tabs and line feeds
formattedValue = formattedValue.Replace("\t", "\\t").Replace("\n", "\\n").Replace("\r", "\\r");
// chop at 80 characters
int length = formattedValue.Length;
if (length > 80)
formattedValue = formattedValue.Substring(0, 80);
if (isString)
formattedValue = string.Format(CultureInfo.InvariantCulture, "\"{0}\"", formattedValue);
if (length > 80)
formattedValue += " (+" + (length - 80) + " chars)";
formattedValue = " = " + formattedValue;
}
writer.WriteLine("{0}{1}{2}{3} [{4}]{5}", indentation, keyPrefix, name, formattedValue, value.GetType(), keyRef);
// 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;
if (value is System.Security.Principal.SecurityIdentifier)
return;
if (!recursiveDump)
return;
PropertyInfo[] properties =
(from property in type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
where property.GetIndexParameters().Length == 0
&& property.CanRead
select property).ToArray();
FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToArray();
if (properties.Length == 0 && fields.Length == 0)
return;
writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}{{", indentation));
if (properties.Length > 0)
{
writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} properties {{", indentation));
foreach (PropertyInfo pi in properties)
{
try
{
object propertyValue = pi.GetValue(value, null);
InternalDump(indentationLevel + 2, pi.Name, propertyValue, writer, idGenerator, true);
}
catch (TargetInvocationException ex)
{
InternalDump(indentationLevel + 2, pi.Name, ex, writer, idGenerator, false);
}
catch (ArgumentException ex)
{
InternalDump(indentationLevel + 2, pi.Name, ex, writer, idGenerator, false);
}
catch (RemotingException ex)
{
InternalDump(indentationLevel + 2, pi.Name, ex, writer, idGenerator, false);
}
}
writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} }}", indentation));
}
if (fields.Length > 0)
{
writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} fields {{", indentation));
foreach (FieldInfo field in fields)
{
try
{
object fieldValue = field.GetValue(value);
InternalDump(indentationLevel + 2, field.Name, fieldValue, writer, idGenerator, true);
}
catch (TargetInvocationException ex)
{
InternalDump(indentationLevel + 2, field.Name, ex, writer, idGenerator, false);
}
}
writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0} }}", indentation));
}
writer.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0}}}", indentation));
}
}
internal static class StringEx
{
/// <summary>
/// Compares the <paramref name="value"/> against <c>null</c> and checks if the
/// string contains only whitespace.
/// </summary>
/// <param name="value">
/// The string value to check.
/// </param>
/// <returns>
/// <c>true</c> if the string <paramref name="value"/> is <c>null</c>, <see cref="string.Empty"/>,
/// or contains only whitespace; otherwise, <c>false</c>.
/// </returns>
public static bool IsNullOrWhiteSpace(string value)
{
return value == null || value.Trim().Length == 0;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment