首页 > 编程语言 >C# helper to dump any object to a log (zz)

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

时间:2023-06-09 17:01:16浏览次数:42  
标签:log dump C# baseName object DumpMemberAttribute members public string


57IV3
C# helper to dump any object to a lo944905381
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 = true, MemberName = member };
                ProcessMember(members, dd, member, o, name);
                count++;
            }
         }
 
        public static List<string> Dump(object o, string baseName)
        {
            RecursionCount = 0;
 
            Dictionary<string, string> members = Enumerate(o, baseName);
 
            int maxLength = members.Keys.Select(x => x.Length).Max();
 
            return members.Select(x => (string.Format("{0} = {1}", x.Key.PadRight(maxLength + 1), x.Value))).ToList();
 
        }
 
        /// <summary>
        /// Take a string and create a version with all wierd characters escaped
        /// </summary>
        /// <param name="input">The string to escape</param>
        /// <returns>The escaped string</returns>
        public static string EscapeString(string input)
        {
            StringBuilder result = new StringBuilder();
            string wellknown = "\r\n\t\0\\";
            string wellknownmap = @"rnt0\";
            int ix;
 
            foreach (char c in input)
            {
 
                ix = wellknown.IndexOf(c);
                if (ix >= 0)
                {
                    result.Append(@"\" + wellknownmap[ix]);
                }
                else if (char.IsControl(c))
                {
                    result.AppendFormat(@"\x{0:x4}", (int)c);
                }
                else
                {
                    result.Append(c);
                }
            }
 
            return result.ToString();
        }
    }
}

To use it, just call Dump(), passing the object to dump and a base name to use in the output.
Apply the DumpMemberAttribute to members to change their behaviour when being processed.
Apply the DumpClassAttribute to classes to do the same.
Objects can also implement the IDebugDumpMask interface in order to customise the visibility 
of their members using more complex logic, if required.
Enjoy!
44905381

IS2120@G57IV3






标签:log,dump,C#,baseName,object,DumpMemberAttribute,members,public,string
From: https://blog.51cto.com/u_16156420/6449459

相关文章

  • 使用Animate和CreateJS设计H5页面
    Animate和CreateJS是常用于HTML5页面设计的工具,通过使用这些工具,可以创建各种动画特效,从而提高交互性和视觉效果。游戏:Animate和CreateJS可以用于创建精彩的网页游戏,比如跑酷类、动作类、益智类等众多不同类型的游戏。这些游戏通常需要丰富的场景设计、角色设定、音效、背景音......
  • 「杂题乱写」AGC 003
    「杂题乱写」AGC003点击查看目录目录「杂题乱写」AGC003A|WannagobackhomeB|SimplifiedmahjongC|BBuBBBlesort!D|AnticubeE|SequentialoperationsonSequenceF|FractionofFractal今日推歌是星尘唱的《光》,是尘2021年的官方生贺曲。马上又要到8.1......
  • vnc 配置 已验证
    配置错误,但是能用[root@rac1sbin]#systemctlstatusvncserver@:2.service-lvncserver@:2.service-Remotedesktopservice(VNC)  Loaded:loaded(/usr/lib/systemd/system/vncserver@:2.service;disabled)  Active:failed(Result:exit-code)sinceSun2015-05-......
  • CVS 用法总结(zz)
    这里有份CVS中文手册http://man.chinaunix.net/develop/cvsdoc_zh/index.html#Topcvs用法总结(1)--cvs命令格式,标志字符和环境cvs用法总结(1)--cvs命令格式读书笔记,中文名"版本控制之道-使用cvs",英文名"PragmaticVersionControl-UsingCVS"。以下内容......
  • c++中的位操作
    c++中的位操作使用or设置位 number|=1<<x;将设置位x清除位使用位操作&清除位。 number&=~(1<<x);这将清除位x。转换位XOR操作(^) number^=1<<x;检查位 bit=number&(1<<x);......
  • 在access 2007 中进行sql query
    1.发现了两个小工具(都是通过odbc来访问)1.1winsql(其中lite是免费版,需要上网站上申请注册号)1.2querytool2.CreatingaSQLQueryinAccess2007byrhyttinenonSeptember17,2008SQL(StructuredQueryLanguage)isapowerfuldatabaselanguageusedinqueries......
  • _STLP_NEW_IO_NAMESPACE
     在vc6环境下,使用stlport时,会出现errorC2653:‘_STLP_NEW_IO_NAMESPACE’:isnotaclassornamespacename 在此种情况下,是因为包含流相关头文件时,使用了这样的格式:#include"iostream.h"或是#include<iostream.h>。而这些文件并不是标准形式,改为#include<iostream>......
  • EC20调试过程备份
    1、模块通电 如图,将单片机引脚(VBAT_EN)拉低,即可给EC20供3.8V电源。2、模块开机如图,单片机引脚拉高500ms,即可让模块开机。TCP步骤:1、给模块上电并开机2、AT+CPIN?查看SIM卡的状态3、AT+CREG?检查是否注册到网络4、AT+CGREG?/AT+CEREG?查询GPRS网络注册状态(2G网络)/......
  • 在 c++ 中删除一个 NULL pointer (空指针)
    @is21201.在c++中删除一个NULLpointer(空指针)是安全的2.标准中相关内容如下:"Ineitheralternative,ifthevalueoftheoperandofdeleteisthenullpointertheoperationhasnoeffect."3.一个有用的宏@is2120#definemy_delete(x){deletex;x=NULL;} 4......
  • assert vs. ASSERT(mfc) vs. VERIFY
       assertvs.ASSERT(mfc)区别   1.首先assert是c标准里的一个宏,而ASSERT是MFC的一个宏。   2.assert可以通过NDEBUG来关闭。   3.assert在debug版本和release版本里都可用,另两个宏_ASSERT和_ASSERTE   只有当_DEBUG标志被定义的情况下才......