Picture by Isabelle Portes

Print Out .NET Classes To String in C#

Ever needed to generate a string that contains the C#-Code of a .NET class? Well, probably not 🙂 But I needed that once and now I want to share my code with you.

So I had a web page to demonstrate an API and I needed to print out .NET classes I actually had running. I didn’t want to manually copy the code from my C# file to the CSHTML view because the C# file was always subjected to changes. Maintaining the C# and the CSHTML in sync appeared cumbersome. That’s why I created this simple little helper that examined a class with reflection and spit out a string containing the class definition in C#. The generated code contains public properties and attributes but no methods, fields or accessor implementation (i.e. it generates a POCO). So if by chance you want to use it, knock yourselves out.

using System;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;

namespace MunirHusseini
{
    public class PocoPrint
    {
        public static string Print(object obj)
        {
            if (obj == null)
            {
                return "null";
            }

            var tab = "    ";
            var type = obj.GetType();
            var sb = new StringBuilder();
            sb.Append(type.IsPublic ? "public" : "internal");
            sb.Append(" class ");
            sb.AppendLine(type.Name);
            sb.AppendLine("{");
            var propSep = string.Empty;
            foreach (var property in type.GetProperties())
            {
                sb.Append(propSep);
                OutputProperty(property, sb, tab);
                propSep = Environment.NewLine;
            }
            sb.AppendLine("}");

            return sb.ToString();
        }

        private static string GetModifier(MethodBase m) =>
            m == null
                ? null
                : m.IsPublic
                    ? "public"
                    : m.IsAssembly
                        ? "internal"
                        : m.IsFamilyOrAssembly
                            ? "protected internal"
                            : m.IsFamily
                                ? "protected"
                                : "private";

        private static object GetPropertyValue(PropertyInfo p, Attribute attribute)
        {
            try
            {
                var value = p.GetValue(attribute);
                if (value == null)
                {
                    return null;
                }
                if (value is bool b)
                {
                    return b == false ? null : "true";
                }
                if (value is string)
                {
                    return $@"""{value}""";
                }
                if (value is char)
                {
                    return $@"'{value}'";
                }
                if (value is IConvertible convertible)
                {
                    return convertible.ToString(CultureInfo.InvariantCulture);
                }

                return value;
            }
            catch
            {
                return null;
            }
        }

        private static string GetTypeName(Type type)
        {
            if (!type.IsGenericType)
            {
                return type.Name;
            }

            var sb = new StringBuilder();

            sb.Append(type.Name.Substring(0, type.Name.IndexOf('`')));
            sb.Append("<");
            var sep = string.Empty;
            foreach (var genericArgument in type.GetGenericArguments())
            {
                sb.Append(sep);
                sb.Append(GetTypeName(genericArgument));
                sep = ", ";
            }
            sb.Append(">");

            return sb.ToString();
        }

        private static void OutputAttribtue(StringBuilder sb, string tab, Attribute attribute)
        {
            var attributeType = attribute.GetType();
            sb.Append(tab);
            sb.Append("[");
            sb.Append(GetTypeName(attributeType).Replace("Attribute", string.Empty));
            var properties = (from p in attributeType.GetProperties()
                              where p.Name != "TypeId"
                              let value = GetPropertyValue(p, attribute)
                              where value != null
                              select $"{p.Name} = {value}").ToList();
            sb.Append("(");
            sb.Append(string.Join(", ", properties));
            sb.Append(")");
            sb.AppendLine("]");
        }

        private static void OutputProperty(PropertyInfo property, StringBuilder sb, string tab)
        {
            foreach (var attribute in property.GetCustomAttributes())
            {
                OutputAttribtue(sb, tab, attribute);
            }

            var getModifier = GetModifier(property.GetMethod);
            var setModifier = GetModifier(property.SetMethod);
            sb.Append(tab);
            sb.Append(getModifier);
            sb.Append(" ");
            sb.Append(GetTypeName(property.PropertyType));
            sb.Append(" ");
            sb.Append(property.Name);

            sb.Append(" {");
            if (property.CanRead)
            {
                sb.Append(" get;");
            }

            if (property.CanWrite)
            {
                if (getModifier != setModifier)
                {
                    sb.Append(" ");
                    sb.Append(setModifier);
                }

                sb.Append(" set;");
            }

            sb.AppendLine(" }");
        }
    }
}

Freelance full-stack .NET and JS developer and architect. Located near Cologne, Germany.

Leave a Reply

Your email address will not be published. Required fields are marked *