1+ namespace Microsoft
2+ {
3+ using System ;
4+ using System . Collections . Generic ;
5+ using System . Diagnostics . Contracts ;
6+ using System . Text ;
7+
8+ // REF: https://raw.githubusercontent.com/aspnet/Common/dev/shared/Microsoft.Extensions.TypeNameHelper.Sources/TypeNameHelper.cs
9+ static class TypeNameHelper
10+ {
11+ static readonly Dictionary < Type , string > BuiltInTypeNames = new Dictionary < Type , string >
12+ {
13+ [ typeof ( void ) ] = "void" ,
14+ [ typeof ( bool ) ] = "bool" ,
15+ [ typeof ( byte ) ] = "byte" ,
16+ [ typeof ( char ) ] = "char" ,
17+ [ typeof ( decimal ) ] = "decimal" ,
18+ [ typeof ( double ) ] = "double" ,
19+ [ typeof ( float ) ] = "float" ,
20+ [ typeof ( int ) ] = "int" ,
21+ [ typeof ( long ) ] = "long" ,
22+ [ typeof ( object ) ] = "object" ,
23+ [ typeof ( sbyte ) ] = "sbyte" ,
24+ [ typeof ( short ) ] = "short" ,
25+ [ typeof ( string ) ] = "string" ,
26+ [ typeof ( uint ) ] = "uint" ,
27+ [ typeof ( ulong ) ] = "ulong" ,
28+ [ typeof ( ushort ) ] = "ushort" ,
29+ } ;
30+
31+ internal static string GetTypeDisplayName ( object item , bool fullName = true ) => item ? . GetType ( ) . GetTypeDisplayName ( fullName ) ;
32+
33+ /// <summary>
34+ /// Pretty print a type name.
35+ /// </summary>
36+ /// <param name="type">The <see cref="Type"/>.</param>
37+ /// <param name="fullName"><c>true</c> to print a fully qualified name.</param>
38+ /// <param name="includeGenericParameterNames"><c>true</c> to include generic parameter names.</param>
39+ /// <returns>The pretty printed type name.</returns>
40+ internal static string GetTypeDisplayName ( this Type type , bool fullName = true , bool includeGenericParameterNames = false )
41+ {
42+ Contract . Requires ( type != null ) ;
43+
44+ var builder = new StringBuilder ( ) ;
45+ ProcessType ( builder , type , new DisplayNameOptions ( fullName , includeGenericParameterNames ) ) ;
46+ return builder . ToString ( ) ;
47+ }
48+
49+ static void ProcessType ( StringBuilder builder , Type type , DisplayNameOptions options )
50+ {
51+ Contract . Requires ( builder != null ) ;
52+ Contract . Requires ( type != null ) ;
53+
54+ if ( type . IsGenericType )
55+ {
56+ var genericArguments = type . GetGenericArguments ( ) ;
57+ ProcessGenericType ( builder , type , genericArguments , genericArguments . Length , options ) ;
58+ }
59+ else if ( type . IsArray )
60+ {
61+ ProcessArrayType ( builder , type , options ) ;
62+ }
63+ else if ( BuiltInTypeNames . TryGetValue ( type , out var builtInName ) )
64+ {
65+ builder . Append ( builtInName ) ;
66+ }
67+ else if ( type . IsGenericParameter )
68+ {
69+ if ( options . IncludeGenericParameterNames )
70+ {
71+ builder . Append ( type . Name ) ;
72+ }
73+ }
74+ else
75+ {
76+ builder . Append ( options . FullName ? type . FullName : type . Name ) ;
77+ }
78+ }
79+
80+ static void ProcessArrayType ( StringBuilder builder , Type type , DisplayNameOptions options )
81+ {
82+ Contract . Requires ( builder != null ) ;
83+ Contract . Requires ( type != null ) ;
84+
85+ var innerType = type ;
86+
87+ while ( innerType . IsArray )
88+ {
89+ innerType = innerType . GetElementType ( ) ;
90+ }
91+
92+ ProcessType ( builder , innerType , options ) ;
93+
94+ while ( type . IsArray )
95+ {
96+ builder . Append ( '[' ) ;
97+ builder . Append ( ',' , type . GetArrayRank ( ) - 1 ) ;
98+ builder . Append ( ']' ) ;
99+ type = type . GetElementType ( ) ;
100+ }
101+ }
102+
103+ static void ProcessGenericType ( StringBuilder builder , Type type , Type [ ] genericArguments , int length , DisplayNameOptions options )
104+ {
105+ Contract . Requires ( builder != null ) ;
106+ Contract . Requires ( type != null ) ;
107+ Contract . Requires ( genericArguments != null ) ;
108+ Contract . Requires ( length > 0 ) ;
109+
110+ var offset = 0 ;
111+
112+ if ( type . IsNested )
113+ {
114+ offset = type . DeclaringType . GetGenericArguments ( ) . Length ;
115+ }
116+
117+ if ( options . FullName )
118+ {
119+ if ( type . IsNested )
120+ {
121+ ProcessGenericType ( builder , type . DeclaringType , genericArguments , offset , options ) ;
122+ builder . Append ( '+' ) ;
123+ }
124+ else if ( ! string . IsNullOrEmpty ( type . Namespace ) )
125+ {
126+ builder . Append ( type . Namespace ) ;
127+ builder . Append ( '.' ) ;
128+ }
129+ }
130+
131+ var genericPartIndex = type . Name . IndexOf ( '`' ) ;
132+
133+ if ( genericPartIndex <= 0 )
134+ {
135+ builder . Append ( type . Name ) ;
136+ return ;
137+ }
138+
139+ builder . Append ( type . Name , 0 , genericPartIndex ) ;
140+ builder . Append ( '<' ) ;
141+
142+ for ( var i = offset ; i < length ; i ++ )
143+ {
144+ ProcessType ( builder , genericArguments [ i ] , options ) ;
145+
146+ if ( i + 1 == length )
147+ {
148+ continue ;
149+ }
150+
151+ builder . Append ( ',' ) ;
152+
153+ if ( options . IncludeGenericParameterNames || ! genericArguments [ i + 1 ] . IsGenericParameter )
154+ {
155+ builder . Append ( ' ' ) ;
156+ }
157+ }
158+
159+ builder . Append ( '>' ) ;
160+ }
161+
162+ struct DisplayNameOptions
163+ {
164+ internal DisplayNameOptions ( bool fullName , bool includeGenericParameterNames )
165+ {
166+ FullName = fullName ;
167+ IncludeGenericParameterNames = includeGenericParameterNames ;
168+ }
169+
170+ public bool FullName { get ; }
171+
172+ public bool IncludeGenericParameterNames { get ; }
173+ }
174+ }
175+ }
0 commit comments