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);
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