Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Create the Friendly Name of Any Type

0.00/5 (No votes)
19 Dec 2015 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.

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:

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.

[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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here