본문 바로가기

Programing/닷넷

ObjectDumper

Linq in Action (거침없이 배우는 LINQ)라는 책에 보면 ObjectDumper를 사용하는 부분이 많이 등장한다.

닷넷의 어떤 객체에 대해 출력을 쉽게하기 위해 만든 유틸리티 클래스인 것 같다.

구현은 리플렉션을 통해 하지 않았을까 추측해본다.


사이트: http://objectdumper.codeplex.com


메뉴탭에서 DOWNLOADS를 눌러도 릴리즈를 가지고 있지 않다(This project has no releases.)라는 메시지가 보인다.

크냥 소스코드를 받아서 빌드했다. ReleaseBinaries 모드의 결과 => ObjectDumper.dll


잘 돌아가나 해서 혹시 테스트를 해보았는데,,, 아래와 비슷하게 만들면 된다.

using ObjectDumper;

 

var c = new Class1();

textBox1.Text = c.Something();

var r = c.DumpToString("test");

MessageBox.Show(r);


Linq in Action에서는 ObjectDumber.Write(object) 식으로 사용을 했는데, ObjectDumber는 네임스페이스로 바뀌고 정적메소드 Dumper가 추가되었다. TextWriter를 사용할 수 있다면 아래처럼 사용하면 될 것이다. (개인적으로 제네릭 버전의 확장 메소드가 좋은 것 같다.)


콘솔 애플리케이션에서는 Dump메소드에 3번째 인자에 Console.Out을 지정하면 콘솔에 바로 출력을 할 수 있다.

using System;

using ObjectDumper;

 

namespace ConsoleApplication1

{

    class Program

    {

        static void Main(string[] args)

        {

            var w = new WhatTheClass();

            w.The = "Class";

            Dumper.Dump(w, "WhatTheClass", Console.Out);

        }

    }

 

    class WhatTheClass

    {

        public string The {get;set;}

        public string What()

        {

            return The;

        }

    }

}


결과



참고로 구현은 InternalDump라는 정적메소드에서 동작을 한다.

System.Reflection을 이용하고, InternalDump를 재귀호출을 하게끔 구현되어 있었다.

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));

}