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 (in other words, print out -NET classes to string)? 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. Subsequently, 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. Below is my code to print out .NET classes to string.

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 *