Unify TypeNameHelpers (dotnet/extensions#876)
\n\nCommit migrated from 2f53c36195
This commit is contained in:
parent
ebb5522f00
commit
bdc2ea81c0
|
|
@ -9,6 +9,8 @@ namespace Microsoft.Extensions.Internal
|
|||
{
|
||||
internal class TypeNameHelper
|
||||
{
|
||||
private const char DefaultNestedTypeDelimiter = '+';
|
||||
|
||||
private static readonly Dictionary<Type, string> _builtInTypeNames = new Dictionary<Type, string>
|
||||
{
|
||||
{ typeof(void), "void" },
|
||||
|
|
@ -40,15 +42,17 @@ namespace Microsoft.Extensions.Internal
|
|||
/// <param name="type">The <see cref="Type"/>.</param>
|
||||
/// <param name="fullName"><c>true</c> to print a fully qualified name.</param>
|
||||
/// <param name="includeGenericParameterNames"><c>true</c> to include generic parameter names.</param>
|
||||
/// <param name="includeGenericParameters"><c>true</c> to include generic parameters.</param>
|
||||
/// <param name="nestedTypeDelimiter">Character to use as a delimiter in nested type names</param>
|
||||
/// <returns>The pretty printed type name.</returns>
|
||||
public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false)
|
||||
public static string GetTypeDisplayName(Type type, bool fullName = true, bool includeGenericParameterNames = false, bool includeGenericParameters = true, char nestedTypeDelimiter = DefaultNestedTypeDelimiter)
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames));
|
||||
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames, includeGenericParameters, nestedTypeDelimiter));
|
||||
return builder.ToString();
|
||||
}
|
||||
|
||||
private static void ProcessType(StringBuilder builder, Type type, DisplayNameOptions options)
|
||||
private static void ProcessType(StringBuilder builder, Type type, in DisplayNameOptions options)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
|
|
@ -72,11 +76,17 @@ namespace Microsoft.Extensions.Internal
|
|||
}
|
||||
else
|
||||
{
|
||||
builder.Append(options.FullName ? type.FullName : type.Name);
|
||||
var name = options.FullName ? type.FullName : type.Name;
|
||||
builder.Append(name);
|
||||
|
||||
if (options.NestedTypeDelimiter != DefaultNestedTypeDelimiter)
|
||||
{
|
||||
builder.Replace(DefaultNestedTypeDelimiter, options.NestedTypeDelimiter, builder.Length - name.Length, name.Length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void ProcessArrayType(StringBuilder builder, Type type, DisplayNameOptions options)
|
||||
private static void ProcessArrayType(StringBuilder builder, Type type, in DisplayNameOptions options)
|
||||
{
|
||||
var innerType = type;
|
||||
while (innerType.IsArray)
|
||||
|
|
@ -95,7 +105,7 @@ namespace Microsoft.Extensions.Internal
|
|||
}
|
||||
}
|
||||
|
||||
private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, DisplayNameOptions options)
|
||||
private static void ProcessGenericType(StringBuilder builder, Type type, Type[] genericArguments, int length, in DisplayNameOptions options)
|
||||
{
|
||||
var offset = 0;
|
||||
if (type.IsNested)
|
||||
|
|
@ -108,7 +118,7 @@ namespace Microsoft.Extensions.Internal
|
|||
if (type.IsNested)
|
||||
{
|
||||
ProcessGenericType(builder, type.DeclaringType, genericArguments, offset, options);
|
||||
builder.Append('+');
|
||||
builder.Append(options.NestedTypeDelimiter);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(type.Namespace))
|
||||
{
|
||||
|
|
@ -126,35 +136,44 @@ namespace Microsoft.Extensions.Internal
|
|||
|
||||
builder.Append(type.Name, 0, genericPartIndex);
|
||||
|
||||
builder.Append('<');
|
||||
for (var i = offset; i < length; i++)
|
||||
if (options.IncludeGenericParameters)
|
||||
{
|
||||
ProcessType(builder, genericArguments[i], options);
|
||||
if (i + 1 == length)
|
||||
builder.Append('<');
|
||||
for (var i = offset; i < length; i++)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
ProcessType(builder, genericArguments[i], options);
|
||||
if (i + 1 == length)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.Append(',');
|
||||
if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter)
|
||||
{
|
||||
builder.Append(' ');
|
||||
builder.Append(',');
|
||||
if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter)
|
||||
{
|
||||
builder.Append(' ');
|
||||
}
|
||||
}
|
||||
builder.Append('>');
|
||||
}
|
||||
builder.Append('>');
|
||||
}
|
||||
|
||||
private struct DisplayNameOptions
|
||||
private readonly struct DisplayNameOptions
|
||||
{
|
||||
public DisplayNameOptions(bool fullName, bool includeGenericParameterNames)
|
||||
public DisplayNameOptions(bool fullName, bool includeGenericParameterNames, bool includeGenericParameters, char nestedTypeDelimiter)
|
||||
{
|
||||
FullName = fullName;
|
||||
IncludeGenericParameters = includeGenericParameters;
|
||||
IncludeGenericParameterNames = includeGenericParameterNames;
|
||||
NestedTypeDelimiter = nestedTypeDelimiter;
|
||||
}
|
||||
|
||||
public bool FullName { get; }
|
||||
|
||||
public bool IncludeGenericParameters { get; }
|
||||
|
||||
public bool IncludeGenericParameterNames { get; }
|
||||
|
||||
public char NestedTypeDelimiter { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -216,6 +216,47 @@ namespace Microsoft.Extensions.Internal
|
|||
Assert.Equal(expected, actual);
|
||||
}
|
||||
|
||||
public static TheoryData<Type, string> FullTypeNameData
|
||||
{
|
||||
get
|
||||
{
|
||||
return new TheoryData<Type, string>
|
||||
{
|
||||
// Predefined Types
|
||||
{ typeof(int), "int" },
|
||||
{ typeof(List<int>), "System.Collections.Generic.List" },
|
||||
{ typeof(Dictionary<int, string>), "System.Collections.Generic.Dictionary" },
|
||||
{ typeof(Dictionary<int, List<string>>), "System.Collections.Generic.Dictionary" },
|
||||
{ typeof(List<List<string>>), "System.Collections.Generic.List" },
|
||||
|
||||
// Classes inside NonGeneric class
|
||||
{ typeof(A), "Microsoft.Extensions.Internal.TypeNameHelperTest.A" },
|
||||
{ typeof(B<int>), "Microsoft.Extensions.Internal.TypeNameHelperTest.B" },
|
||||
{ typeof(C<int, string>), "Microsoft.Extensions.Internal.TypeNameHelperTest.C" },
|
||||
{ typeof(C<int, B<string>>), "Microsoft.Extensions.Internal.TypeNameHelperTest.C" },
|
||||
{ typeof(B<B<string>>), "Microsoft.Extensions.Internal.TypeNameHelperTest.B" },
|
||||
|
||||
// Classes inside Generic class
|
||||
{ typeof(Outer<int>.D), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.D" },
|
||||
{ typeof(Outer<int>.E<int>), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.E" },
|
||||
{ typeof(Outer<int>.F<int, string>), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.F" },
|
||||
{ typeof(Outer<int>.F<int, Outer<int>.E<string>>),"Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.F" },
|
||||
{ typeof(Outer<int>.E<Outer<int>.E<string>>), "Microsoft.Extensions.Internal.TypeNameHelperTest.Outer.E" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[MemberData(nameof(FullTypeNameData))]
|
||||
public void Can_PrettyPrint_FullTypeName_WithoutGenericParametersAndNestedTypeDelimiter(Type type, string expectedTypeName)
|
||||
{
|
||||
// Arrange & Act
|
||||
var displayName = TypeNameHelper.GetTypeDisplayName(type, fullName: true, includeGenericParameters: false, nestedTypeDelimiter: '.');
|
||||
|
||||
// Assert
|
||||
Assert.Equal(expectedTypeName, displayName);
|
||||
}
|
||||
|
||||
private class A { }
|
||||
|
||||
private class B<T> { }
|
||||
|
|
|
|||
Loading…
Reference in New Issue