Skip to content

Instantly share code, notes, and snippets.

@bymyslf
Created June 23, 2016 08:23
Show Gist options
  • Save bymyslf/5d6e0b5f50bcec4c6fd24c79a0ba111b to your computer and use it in GitHub Desktop.
Save bymyslf/5d6e0b5f50bcec4c6fd24c79a0ba111b to your computer and use it in GitHub Desktop.
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
internal class ObjectRenderer
{
private TextWriter writer;
private static readonly Func<PropertyInfo, object, object> propertyGetter;
static ObjectRenderer()
{
propertyGetter = OpenInstancePropertyAcessor();
}
private ObjectRenderer(TextWriter writer)
{
this.writer = writer;
}
private void RenderObject(object obj)
{
if (obj == null)
{
writer.Write("null");
return;
}
if (obj is ValueType || obj is string)
{
writer.Write(obj.ToString());
return;
}
if (obj is DateTime)
{
writer.Write(((DateTime)obj).ToShortDateString());
return;
}
Array objArray = obj as Array;
if (objArray != null)
{
RenderArray(objArray);
return;
}
// Test if we are dealing with some form of collection object
IEnumerable objEnumerable = obj as IEnumerable;
if (objEnumerable != null)
{
// Get a collection interface if we can as its .Count property may be more
// performant than getting the IEnumerator object and trying to advance it.
ICollection objCollection = obj as ICollection;
if (objCollection != null && objCollection.Count == 0)
{
writer.Write("{}");
return;
}
// This is a special check to allow us to get the enumerator from the IDictionary
// interface as this guarantees us DictionaryEntry objects. Note that in .NET 2.0
// the generic IDictionary<> interface enumerates KeyValuePair objects rather than
// DictionaryEntry ones. However the implementation of the plain IDictionary
// interface on the generic Dictionary<> still returns DictionaryEntry objects.
IDictionary objDictionary = obj as IDictionary;
if (objDictionary != null)
{
RenderEnumerator(objDictionary.GetEnumerator());
return;
}
RenderEnumerator(objEnumerable.GetEnumerator());
return;
}
IEnumerator objEnumerator = obj as IEnumerator;
if (objEnumerator != null)
{
RenderEnumerator(objEnumerator);
return;
}
if (obj is DictionaryEntry)
{
RenderDictionaryEntry((DictionaryEntry)obj);
return;
}
Type objType = obj.GetType();
writer.Write(objType.FullName);
writer.Write(":");
writer.Write("{");
MemberInfo[] members = objType.GetMembers(BindingFlags.Public | BindingFlags.Instance);
int pos = 0;
foreach (MemberInfo member in members)
{
FieldInfo fieldInfo = member as FieldInfo;
PropertyInfo propertyInfo = member as PropertyInfo;
if (fieldInfo != null || propertyInfo != null)
{
if (pos++ > 0)
{
writer.Write(", ");
}
writer.Write(member.Name);
writer.Write("=");
object value = fieldInfo != null ? fieldInfo.GetValue(obj) : propertyGetter(propertyInfo, obj);
if (value != null)
{
Type type = fieldInfo != null ? fieldInfo.FieldType : propertyInfo.PropertyType;
bool isReferenceType = !(type.IsValueType || type == typeof(string));
if (isReferenceType)
{
writer.Write("{");
}
RenderObject(value);
if (isReferenceType)
{
writer.Write("}");
}
}
}
}
writer.Write("}");
}
private void RenderArray(Array array)
{
//If the array is not one dimensional
if (array.Rank != 1)
{
writer.Write(array.ToString());
}
else
{
writer.Write(array.GetType().Name + " {");
int len = array.Length;
if (len > 0)
{
RenderObject(array.GetValue(0));
for (int i = 1; i < len; i++)
{
writer.Write(", ");
RenderObject(array.GetValue(i));
}
}
writer.Write("}");
}
}
private void RenderEnumerator(IEnumerator enumerator)
{
writer.Write("{");
if (enumerator != null && enumerator.MoveNext())
{
RenderObject(enumerator.Current);
while (enumerator.MoveNext())
{
writer.Write(", ");
RenderObject(enumerator.Current);
}
}
writer.Write("}");
}
private void RenderDictionaryEntry(DictionaryEntry entry)
{
RenderObject(entry.Key);
writer.Write("=");
RenderObject(entry.Value);
}
// Create open instance delegate to make reflection faster
// More: http://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx
// https://www.simple-talk.com/blogs/2010/07/27/introduction-to-open-instance-delegates/
private static Func<PropertyInfo, object, object> OpenInstancePropertyAcessor()
{
var methodInfo = typeof(PropertyInfo).GetMethod("GetValue", new Type[] { typeof(Object) });
var del = Delegate.CreateDelegate(typeof(Func<PropertyInfo, object, object>), methodInfo);
return del as Func<PropertyInfo, object, object>;
}
internal static void Render(object element)
{
Render(element, Console.Out);
}
internal static void Render(object element, TextWriter writer)
{
ObjectRenderer renderer = new ObjectRenderer(writer);
renderer.RenderObject(element);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment