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
|
internal class TypeNameHelper
|
||||||
{
|
{
|
||||||
|
private const char DefaultNestedTypeDelimiter = '+';
|
||||||
|
|
||||||
private static readonly Dictionary<Type, string> _builtInTypeNames = new Dictionary<Type, string>
|
private static readonly Dictionary<Type, string> _builtInTypeNames = new Dictionary<Type, string>
|
||||||
{
|
{
|
||||||
{ typeof(void), "void" },
|
{ typeof(void), "void" },
|
||||||
|
|
@ -40,15 +42,17 @@ namespace Microsoft.Extensions.Internal
|
||||||
/// <param name="type">The <see cref="Type"/>.</param>
|
/// <param name="type">The <see cref="Type"/>.</param>
|
||||||
/// <param name="fullName"><c>true</c> to print a fully qualified name.</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="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>
|
/// <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();
|
var builder = new StringBuilder();
|
||||||
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames));
|
ProcessType(builder, type, new DisplayNameOptions(fullName, includeGenericParameterNames, includeGenericParameters, nestedTypeDelimiter));
|
||||||
return builder.ToString();
|
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)
|
if (type.IsGenericType)
|
||||||
{
|
{
|
||||||
|
|
@ -72,11 +76,17 @@ namespace Microsoft.Extensions.Internal
|
||||||
}
|
}
|
||||||
else
|
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;
|
var innerType = type;
|
||||||
while (innerType.IsArray)
|
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;
|
var offset = 0;
|
||||||
if (type.IsNested)
|
if (type.IsNested)
|
||||||
|
|
@ -108,7 +118,7 @@ namespace Microsoft.Extensions.Internal
|
||||||
if (type.IsNested)
|
if (type.IsNested)
|
||||||
{
|
{
|
||||||
ProcessGenericType(builder, type.DeclaringType, genericArguments, offset, options);
|
ProcessGenericType(builder, type.DeclaringType, genericArguments, offset, options);
|
||||||
builder.Append('+');
|
builder.Append(options.NestedTypeDelimiter);
|
||||||
}
|
}
|
||||||
else if (!string.IsNullOrEmpty(type.Namespace))
|
else if (!string.IsNullOrEmpty(type.Namespace))
|
||||||
{
|
{
|
||||||
|
|
@ -126,35 +136,44 @@ namespace Microsoft.Extensions.Internal
|
||||||
|
|
||||||
builder.Append(type.Name, 0, genericPartIndex);
|
builder.Append(type.Name, 0, genericPartIndex);
|
||||||
|
|
||||||
builder.Append('<');
|
if (options.IncludeGenericParameters)
|
||||||
for (var i = offset; i < length; i++)
|
|
||||||
{
|
{
|
||||||
ProcessType(builder, genericArguments[i], options);
|
builder.Append('<');
|
||||||
if (i + 1 == length)
|
for (var i = offset; i < length; i++)
|
||||||
{
|
{
|
||||||
continue;
|
ProcessType(builder, genericArguments[i], options);
|
||||||
}
|
if (i + 1 == length)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
builder.Append(',');
|
builder.Append(',');
|
||||||
if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter)
|
if (options.IncludeGenericParameterNames || !genericArguments[i + 1].IsGenericParameter)
|
||||||
{
|
{
|
||||||
builder.Append(' ');
|
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;
|
FullName = fullName;
|
||||||
|
IncludeGenericParameters = includeGenericParameters;
|
||||||
IncludeGenericParameterNames = includeGenericParameterNames;
|
IncludeGenericParameterNames = includeGenericParameterNames;
|
||||||
|
NestedTypeDelimiter = nestedTypeDelimiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool FullName { get; }
|
public bool FullName { get; }
|
||||||
|
|
||||||
|
public bool IncludeGenericParameters { get; }
|
||||||
|
|
||||||
public bool IncludeGenericParameterNames { get; }
|
public bool IncludeGenericParameterNames { get; }
|
||||||
|
|
||||||
|
public char NestedTypeDelimiter { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -216,6 +216,47 @@ namespace Microsoft.Extensions.Internal
|
||||||
Assert.Equal(expected, actual);
|
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 A { }
|
||||||
|
|
||||||
private class B<T> { }
|
private class B<T> { }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue