5

I can't believe that this hasn't been asked before but as I am unable to find this particular "needle" in the stackoverflow haystack I'm asking it.

I would like a neater implementation of GetType().ToString() for closed generic types. By neater I mean that I would like the ToString() call to return a string in the format I would have typed into the development environment.

The default GetType().ToString() on a closed generic will place `n where n is the number of types, and uses [ ]'s instead of <>'s.

Anyway ... to the code.

given the following classes:

A a = new A();
C<A> c = new C<A>();
D<A, B> d = new D<A, B>();
E<B, C<A>, D<A, B>> e = new E<B, C<A>, D<A, B>>();

The default .ToString() returns:

AAA.Events.A
AAA.Events.C`1[AAA.Events.A]
AAA.Events.D`2[AAA.Events.A,AAA.Events.B]
AAA.Events.E`3[AAA.Events.B,AAA.Events.C`1[AAA.Events.A],AAA.Events.D`2

I've written some basic old school (pre linq) code to produce:

AAA.Events.A
AAA.Events.C<AAA.Events.A>
AAA.Events.D<AAA.Events.A,AAA.Events.B>
AAA.Events.E<AAA.Events.B,AAA.Events.C<AAA.Events.A>,AAA.Events.D<AAA.Events.A,AAA.Events.B>>

But I would really like it to return:

A
C<A>
D<A,B>
E<B,C<A>,D<A,B>>

Here's the shoddy code I have written to demonstrate my intent:

public static string NeaterString(this Type t)
{
    string neater = t.ToString();
    if (neater.Contains('`'))
    {
        while (neater.Contains('`'))
        {
            neater = neater.Remove(neater.IndexOf('`'), 2);
        }
        return neater.Replace('[', '<').Replace(']', '>');
    }
    else return neater; 
}
qujck
  • 14,388
  • 4
  • 45
  • 74
  • 1
    It has been asked. http://stackoverflow.com/questions/2579734/get-the-type-name – Adam Sills Jan 11 '13 at 18:45
  • I notice that you're not including nested generic types in your test cases. I'd also be a bit concerned about what your method does with arrays. – Eric Lippert Jan 11 '13 at 19:36
  • My code is more akin to an amateur VBA hack than real code and I promise you I've no intention of using it. I wrote it to express the question and was looking for the Linq-i-fied solution. Out of interest, what is a nested generic type if not E,D>. – qujck Jan 11 '13 at 19:55
  • 1
    `class A { public class B { } }` -- what is the result of `(new A.B()).GetType().ToString()`? What is the result of passing the same type to `NeaterString`? – Eric Lippert Jan 11 '13 at 21:37

2 Answers2

13

I would try to avoid as much string parsing as possible. My routine looks like this:

public static string GetFriendlyName(Type type)
{
    if (type.IsGenericType)
        return string.Format("{0}<{1}>", type.Name.Split('`')[0], string.Join(", ", type.GetGenericArguments().Select(x => GetFriendlyName(x))));
    else
        return type.Name;
}
Kirk Woll
  • 76,112
  • 22
  • 180
  • 195
  • You can avoid allocation of a lambda by passing the method group directly to `Select`. i.e.: `.Select(GetFriendlyName)`. – Drew Noakes May 13 '17 at 17:54
0

More of a shotgun approach:

public static class TypeExtensions
{
    public static string PrettyPrint(this Type type)
    {
        var str = type.ToString();

        for (int i = 1; i < 6; i++)
        {
            str = str.Replace($"`{i}[", "<");
        }

        str = str.Replace("]", ">");

        var knownNamespaces = new[]
        {
            "System.",
            "My.Models.",
            "Domain."
        };

        foreach (var knownNamespace in knownNamespaces)
        {
            str = str.Replace(knownNamespace, string.Empty);
        }

        return str;
    }
}

You can increase the limit on i if you have super complex generics that will exceed `5[

Chris Marisic
  • 32,487
  • 24
  • 164
  • 258