Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C#
Tip/Trick

User-friendly names for Types

Rate me:
Please Sign up or sign in to vote.
4.84/5 (11 votes)
26 Jan 2015CPOL2 min read 35.2K   238   20   10
How to display a Type name in a user-friendly format.

Introduction

The Name property of the Type class provides us with a name that most of the time does not correspond with what we type in code. For instance, a List<string> is really a List`1[String] (without the namespaces). This article provides an extension method that generates a user-friendly name for any given Type. It uses recursion to handle any name step-by-step. It will for instance take the type Tuple<string, double> and recursively find Tuple, double, and string and actually return 'Tuple<string, double>' and not 'Tuple`2[String, Double]'.

Using the code

The method, dubbed FriendlyName, allows four options to tweak the output:

  • useKeywords -- Indicates whether Types that are represented in C# by keywords should be written as such. E.g., int instead of Int32.
  • showGenericArguments -- Applies only to Generic Type Definitions (List<> and such). Indicates whether their Type parameters should be included. E.g., List<T> instead of just List<>. If true, the keywords 'in' and 'out' indicating variance will also be included. E.g., IEnumerable<out T>.
  • showDeclaringType -- Applies to nested types. Indicates whether they should be preceded by their declaring Type. E.g., MyType.NestedType instead of just NestedType.
  • compactNullable -- Indicates whether Nullable types should be written with a '?'. E.g., int? instead of Nullable<int>.

Different types of Types

Some notes on different 'types' of Types and how they are handled:

Generic Types

Generic Types have a name that ends in a back-tick and a number indicating how many Type parameters it contains. These are stripped because they are not really part of the name. (We want List, not List`1.)

Anonymous Types

Anonymous Types are implemented as generic types, so they also have a name that ends in a back-tick and a number. These are stripped, but the rest of the name is left intact. They have rather ugly names, like <>f__AnonymousType0<string, int> but that is by design, so nothing can be done to make their names more readable.

Pointers and Arrays

Pointers have a name that ends in '*'. Pointers need special handling because their element Type might be a 'special' type as well. A long* for instance is not recognized as a 'keyword type'. Depending on parameters, it must be displayed as either long* or Int64*. This is done recursively: first the long type is processed and then the '*' is added. The same goes for arrays.

Unhandled problems

The C# Type system is a complex thing. This creates some problems for FriendlyName that are not handled:

Jagged arrays

Jagged arrays are reversed. We type char[][,], but the actual type is char[,][]

Generic Types

Nested Generic Types in particular are tricky. If we take Dictionary<string, double>.Enumerator for instance, the actual type is Dictionary<TKey, TValue>.Enumerator<string, double>!

Example of use

C#
var c = Enumerable.Repeat(0, 1).GetEnumerator();
var person = new { Name = "Someone", Age = 30 };
int? i = 1;
var tuple = Tuple.Create("", i, 
     new List<Dictionary<string, char[,]>>(), person);

Type[] types =
{
    c.GetType(),
    typeof(IEnumerable<>),
    typeof(char[][,,]),
    tuple.GetType(),
    typeof(Func<int, string, int?, List<string>>),
    typeof(Func<int, string, int?, List<string>>).GetGenericTypeDefinition(),
    typeof(Dictionary<string, double>.Enumerator),
    typeof(Nullable<>)
};

foreach (Type type in types) {
    Console.WriteLine(type.FriendlyName());
    Console.WriteLine(type.FriendlyName(useKeywords: false, showGenericArguments: false, compactNullable: false));
}

//Output:
//
//Enumerable.<RepeatIterator>d__bc<int>
//Enumerable.<RepeatIterator>d__bc<Int32>
//IEnumerable<out T>
//IEnumerable<>
//char[,,][]
//Char[,,][]
//Tuple<string, int?, List<Dictionary<string, char[,]>>, <>f__AnonymousType0<string, int>>
//Tuple<String, Nullable<Int32>, List<Dictionary<String, Char[,]>>, <>f__AnonymousType0<String, Int32>>
//Func<int, string, int?, List<string>>
//Func<Int32, String, Nullable<Int32>, List<String>>
//Func<in T1, in T2, in T3, out TResult>
//Func<,,,>
//Dictionary<TKey, TValue>.Enumerator<string, double>
//Dictionary<,>.Enumerator<String, Double>
//T?
//Nullable<>

 

History

  • July 16th, 2013 -- First posting.
  • January 26th, 2015 -- Fixed a bug and updated text of the article.

License

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


Written By
Software Developer (Junior)
Netherlands Netherlands
Software developer with a bachelor degree and a few years experience (studied computer science at a university as well, but didn't finish). Really love C#, but work usually involves Java. Some experience with VB and C as well.

When hobbying I almost exclusively use C# and AutoHotkey. In a previous life I did some Haskell and Modula-3 and similar stuff. I enjoy games and solving puzzles like on http://projecteuler.net/ (I think I'm stuck at level 4, though)

Comments and Discussions

 
SuggestionGo back?? Pin
elios2642-Jun-16 18:36
elios2642-Jun-16 18:36 
GeneralRe: Go back?? Pin
Dennis_E2-Jun-16 23:13
professionalDennis_E2-Jun-16 23:13 
GeneralWhere to use this code? Pin
Shovon Joarder27-Jan-15 1:57
Shovon Joarder27-Jan-15 1:57 
QuestionBUG! Pin
Jens Madsen, Højby19-Jan-15 14:39
Jens Madsen, Højby19-Jan-15 14:39 
Nice and usewful piece

You need to check the index here:

C#
if (type.IsGenericType) {
              isBasic = false;
              string name = type.Name;
              int index = name.IndexOf('`');
              name = name.Substring(0, index);



For instance, 'Dictionary<tkey, tvalue="">.Enumerator' throws an exception.

5 - or 4 maybe;)
AnswerRe: BUG! Pin
Dennis_E25-Jan-15 22:05
professionalDennis_E25-Jan-15 22:05 
GeneralAwesome Pin
lookitstony25-Jul-14 5:04
lookitstony25-Jul-14 5:04 
GeneralMy vote of 4 Pin
Amir Mohammad Nasrollahi14-Aug-13 23:41
professionalAmir Mohammad Nasrollahi14-Aug-13 23:41 
GeneralMy vote of 5 Pin
Paulo Zemek14-Aug-13 11:36
mvaPaulo Zemek14-Aug-13 11:36 
GeneralMy vote of 5 Pin
_Vitor Garcia_17-Jul-13 4:09
_Vitor Garcia_17-Jul-13 4:09 
GeneralRe: My vote of 5 Pin
Dennis_E17-Jul-13 22:28
professionalDennis_E17-Jul-13 22:28 

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.