Click here to Skip to main content
15,891,976 members
Articles / DevOps
Tip/Trick

Create the Friendly Name of Any Type

Rate me:
Please Sign up or sign in to vote.
4.64/5 (6 votes)
19 Dec 2015CPOL1 min read 9K   2   1
This tip provides you with a solution to get the friendly name of any Type without namespaces.

Introduction

Sometimes, you need the name of a C# Type. That, for example, could be the case if you want to generate C# code, add the name to a logfile, or display it on the UI. It's not too easy to get a friendly name of the Type as you write it in your Visual Studio, especially when you have a generic or an array Type.

I'd like to show you a small class which provides you the power to create a friendly name of any Type.

The Code

The following code is all you need.

C#
public static class TypeExtensions
{
    private static Dictionary<Type, string> m_TypesToFriendlyNames = new Dictionary<Type, string>
    {
        {typeof(bool), "bool"},
        {typeof(byte), "byte"},
        {typeof(sbyte), "sbyte"},
        {typeof(char), "char"},
        {typeof(decimal), "decimal"},
        {typeof(double), "double"},
        {typeof(float), "float"},
        {typeof(int), "int"},
        {typeof(uint), "uint"},
        {typeof(long), "long"},
        {typeof(ulong), "ulong"},
        {typeof(object), "object"},
        {typeof(short), "short"},
        {typeof(ushort), "ushort"},
        {typeof(string), "string"}
    };
        
    public static string GetFriendlyName(this Type type)
    {
        if (type.IsArray)
            return type.GetFriendlyNameOfArrayType();
        if (type.IsGenericType)
            return type.GetFriendlyNameOfGenericType();
        if (type.IsPointer)
            return type.GetFriendlyNameOfPointerType();              
        var aliasName = default(string);
        return m_TypesToFriendlyNames.TryGetValue(type, out aliasName)
            ? aliasName
            : type.Name;
    }
        
    private static string GetFriendlyNameOfArrayType(this Type type)
    {
        var arrayMarker = string.Empty;
        while (type.IsArray)
        {
            var commas = new string(Enumerable.Repeat(',', type.GetArrayRank() - 1).ToArray());
            arrayMarker += $"[{commas}]";
            type = type.GetElementType();
        }
        return type.GetFriendlyName() + arrayMarker;
    }
        
    private static string GetFriendlyNameOfGenericType(this Type type)
    {
        if (type.GetGenericTypeDefinition() == typeof(Nullable<>))
            return type.GetGenericArguments().First().GetFriendlyName() + "?";
        var friendlyName = type.Name;
        var indexOfBacktick = friendlyName.IndexOf('`');
        if (indexOfBacktick > 0)
            friendlyName = friendlyName.Remove(indexOfBacktick);
        var typeParameterNames = type
            .GetGenericArguments()
            .Select(typeParameter => typeParameter.GetFriendlyName());
        var joinedTypeParameters = string.Join(", ", typeParameterNames);
        return string.Format("{0}<{1}>", friendlyName, joinedTypeParameters);
    }

    private static string GetFriendlyNameOfPointerType(this Type type) =>
        type.GetElementType().GetFriendlyName() + "*";        
}

Using the Code

The use is easy:

C#
var typeName = typeof(IEnumerable<string>).GetFriendlyName();
Console.WriteLine(typeName);

// This example displays the following output:
// IEnumerable<string>

Tests

I wrote some MSpec tests for proof, which I'd also like to share with you.

C#
[Subject(typeof(TypeExtensions))]
internal class When_generating_friendly_type_name
{
    It should_generate_the_friendly_name_for_string = () =>
        typeof(string)
        .GetFriendlyName()
        .ShouldEqual("string");

    It should_generate_the_friendly_name_for_long = () =>
        typeof(long)
        .GetFriendlyName()
        .ShouldEqual("long");

    It should_generate_the_friendly_name_for_Int32 = () =>
        typeof(Int32)
        .GetFriendlyName()
        .ShouldEqual("int");

    It should_generate_the_friendly_name_for_object = () =>
        typeof(Object)
        .GetFriendlyName()
        .ShouldEqual("object");

    It should_generate_the_friendly_name_of_pointer = () =>
        typeof(long*)
        .GetFriendlyName()
        .ShouldEqual("long*");

    It should_generate_the_friendly_name_of_nullable = () =>
        typeof(decimal?)
        .GetFriendlyName()
        .ShouldEqual("decimal?");

    It should_generate_the_friendly_name_of_array_type = () =>
        typeof(int[])
        .GetFriendlyName()
        .ShouldEqual("int[]");

    It should_generate_the_friendly_name_of_array_of_arrays_type = () =>
        typeof(sbyte[][][])
        .GetFriendlyName()
        .ShouldEqual("sbyte[][][]");

    It should_generate_the_friendly_name_of_multidimensional_array_type = () =>
        typeof(sbyte[,,,,])
        .GetFriendlyName()
        .ShouldEqual("sbyte[,,,,]");

    It should_generate_the_friendly_name_of_complex_array_type = () =>
        typeof(int[,][,,][,,,])
        .GetFriendlyName()
        .ShouldEqual("int[,][,,][,,,]");

    It should_generate_the_friendly_name_of_generic_type = () =>
        typeof(IEnumerable<Exception>)
        .GetFriendlyName()
        .ShouldEqual("IEnumerable<Exception>");

    It should_generate_the_friendly_name_of_generic_type_with_simple_type_parameter = () =>
        typeof(IEnumerable<short>)
        .GetFriendlyName()
        .ShouldEqual("IEnumerable<short>");

    It should_generate_the_friendly_name_of_complex_generic_type = () =>
        typeof(Dictionary<double, IEnumerable<Dictionary<ulong, 
        Tuple<float, IList<string>, byte, uint>>>>)
        .GetFriendlyName()
        .ShouldEqual("Dictionary<double, IEnumerable<Dictionary<ulong, 
        Tuple<float, IList<string>, byte, uint>>>>");

    It should_generate_the_friendly_name_of_combined_generic_array_type = () =>
        typeof(Dictionary<double[][,], IEnumerable<string>[]>[,,,])
        .GetFriendlyName()
        .ShouldEqual("Dictionary<double[][,], IEnumerable<string>[]>[,,,]");
}

Points of Interest

This functionality is also part of my Nuget package WuffProjects.CodeGeneration so if you want to use it for code generation, you might want to use this.

If you know any better function which passes all of my tests, I'd love to read your solution! If you know any Type whose name I cannot resolve with that function, I'd also like to know of that. Please leave your comment.

Thanks for reading!

History

  • 19.12.2015: Added the tip

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
Eric Ouellet12-Jun-23 10:01
professionalEric Ouellet12-Jun-23 10:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.