现在的位置: 首页 > 综合 > 正文

C# helper to dump any object to a log (zz)

2013年01月19日 ⁄ 综合 ⁄ 共 6431字 ⁄ 字号 评论关闭

IS2120@CSDN.BG57IV3
C# helper to dump any object to a log
//z 2012-08-27 11:06:42 IS2120@csdn.T1944905381
Published 19/12/2011 c# , code Leave a Comment
Tags: c#, debug, dump object, reflection
Some time back I had the need for code to make a reasonable go at dumping out any object into
a debug log. Visual Studio does a good job of inspecting objects, so I figured there was
probably a clever trick somewhere to achieve this easily, but I couldn’t find one. In the end
, I wrote my own, and learned a bit more about reflection in the process.
My first attempt was useful but a bit fragile. Often it would fail horribly on a new class and
I’d have to decorate the class with my custom attributes to make it work.
Since then I’ve refined it a fair amount, and it seems pretty stable although I’m sure it’s
neither complete nor perfect. You get better output if you use the custom attributes here and
there, but they shouldn’t be required. I use it in lots of my code and works well for me, so
I share it here in case it’s more widely useful.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using Common.Logging;

namespace DebugTools
{
public interface IDebugDumpMask
{
bool GetMask(string MemberName);
}

[AttributeUsage(AttributeTargets.Class)]
public class DumpClassAttribute : System.Attribute
{

public DumpClassAttribute()
{
IsEnumerable = false;
ForceUseToString = false;
}

/// <summary>
/// Forces the class to be treated as an enumerable type. Default false.
/// </summary>
public bool IsEnumerable { get; set; }
/// <summary>
/// Forces a simple string conversion of the object. Default false.
/// </summary>
public bool ForceUseToString { get; set; }

}

// Note: Visibility takes priority over masking
// If an member is not visible, it will never be included.
// A visible member can be masked out.

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public class DumpMemberAttribute : System.Attribute
{
public DumpMemberAttribute()
{
IsVisible = true;
MemberName = null;
IsEnumerable = false;
ForceUseToString = false;
EscapeString = false;
}

/// <summary>
/// Controls whether the member is included in the output or not. Default true for public members, false for private ones.
/// </summary>
public bool IsVisible { get; set; }
/// <summary>
/// Overrides the automatically derived memeber name
/// </summary>
public string MemberName { get; set; }
/// <summary>
/// Forced the member to be treated as an enumerable type. Default false.
/// </summary>
public bool IsEnumerable { get; set; }
/// <summary>
/// Forces a simple string conversion of the member. Default false.
/// </summary>
public bool ForceUseToString { get; set; }
/// <summary>
/// If true, the string is escaped before outputting.
/// </summary>
public bool EscapeString { get; set; }
}

public class DebugDumper
{
private static int RecursionCount = 0;

private static DumpClassAttribute GetDebugDumpClassAttribute(Type cls)
{
object[] attributes = cls.GetCustomAttributes(typeof(DumpClassAttribute), true);
foreach (object attr in attributes)
{
if (attr is DumpClassAttribute)
return (DumpClassAttribute)attr;
}
return null;

}

private static DumpMemberAttribute GetDebugDumpAttribute(MemberInfo member)
{
object[] attributes = member.GetCustomAttributes(typeof(DumpMemberAttribute), true);
foreach (object attr in attributes)
{
if (attr is DumpMemberAttribute)
return (DumpMemberAttribute)attr;
}
return null;

}

private static Dictionary<string, string> Enumerate(object o, string baseName)
{
//logger.Trace("Enumerate {0}",baseName);

RecursionCount++;

if (RecursionCount > 5)
Debugger.Break();

var members = new Dictionary<string, string>();

var ddc = GetDebugDumpClassAttribute(o.GetType());

bool ForceEnum = CheckForcedEnumerable(o.GetType());

if (ForceEnum || (ddc != null && ddc.IsEnumerable))
{
ProcessEnumerable(members, o, baseName);
}
else if (ddc != null && ddc.ForceUseToString)
{
members.Add(baseName, o.ToString());
}
else
{

BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;

FieldInfo[] fi = o.GetType().GetFields(flags);
PropertyInfo[] pi = o.GetType().GetProperties(flags);

ProcessFields(o, baseName, members, fi);
ProcessProperties(o, baseName, members, pi);
}

RecursionCount--;

return members;
}

private static bool CheckForcedEnumerable(Type type)
{
if (type.FullName.StartsWith("System.Collections.Generic.List"))
return true;
if (type.FullName.StartsWith("System.Collections.Generic.Dictionary"))
return true;
if (type.IsArray)
return true;
return false;
}

private static void ProcessProperties(object o, string baseName, Dictionary<string, string> members, PropertyInfo[] pi)
{
DumpMemberAttribute dd;
IDebugDumpMask masker = o as IDebugDumpMask;
bool mask;

foreach (PropertyInfo prop in pi)
{
// Default is to show properties always
dd = GetDebugDumpAttribute(prop) ?? new DumpMemberAttribute() { MemberName = prop.Name, IsVisible = true };
mask = masker == null ? true : masker.GetMask(prop.Name);

if (dd.IsVisible && mask)
{
int IndexerCount = prop.GetIndexParameters().Count();
if (IndexerCount == 0 || (dd != null && dd.IsEnumerable))
try
{
ProcessMember(members, dd, prop.Name, prop.GetValue(o, null), baseName);
}
catch (TargetInvocationException)
{
}

else
Debug.Assert(false, "Can't dump an indexed property!");

}
}
}

private static void ProcessFields(object o, string baseName, Dictionary<string, string> members, FieldInfo[] fi)
{
DumpMemberAttribute dd;
IDebugDumpMask masker = o as IDebugDumpMask;
bool mask;

foreach (FieldInfo field in fi)
{
// The attribute might be null, so we need to get some defaults if it is
dd = GetDebugDumpAttribute(field) ?? new DumpMemberAttribute() { MemberName = field.Name, IsVisible = field.IsPublic };
mask = masker == null ? true : masker.GetMask(field.Name);

if (dd.IsVisible && mask)
{
try
{
ProcessMember(members, dd, field.Name, field.GetValue(o), baseName);
}
catch (TargetInvocationException)
{
}

}
}
}

private static void ConcatSubMembers(Dictionary<string, string> members, Dictionary<string, string> subMembers)
{
foreach (KeyValuePair<string, string> item in subMembers)
{
members.Add(item.Key, item.Value);
}
}

private static void ProcessMember(Dictionary<string, string> members, DumpMemberAttribute attribute, string memberName, object value, string baseName)
{
//logger.Trace("ProcessMember {0} : {1}", baseName, memberName);

string name = string.Format("{0} : {1}", baseName, attribute.MemberName ?? memberName);

if (value == null)
{
members.Add(name, "<null>");
}
else
{
Type type = value.GetType();
if (type.IsArray || attribute.IsEnumerable)
{
ProcessEnumerable(members, value, name);
}
else if (type.IsValueType || type == typeof(System.String) || attribute.ForceUseToString)
{
members.Add(name, attribute.EscapeString ? EscapeString(value.ToString()) : value.ToString());
}
else if (type.IsClass)
{
var subMembers = Enumerate(value, name);
ConcatSubMembers(members, subMembers);
}
else if (type.IsInterface)
{
members.Add(name, type.ToString());
}
}
}

private static void ProcessEnumerable(Dictionary<string, string> members, object value, string name)
{
IEnumerable e = value as IEnumerable;
value.GetType();
int count = 0;

foreach (object o in e.Cast<object>())
{
var member = string.Format("[{0}]", count);
var dd = new DumpMemberAttribute() { IsVisible =

抱歉!评论已关闭.