/// <summary> /// Gets a string representation of this instance. /// </summary> /// <param name="type">The <see cref="Type"/> of which a string representation is requested.</param> /// <param name="options">Any combination of formatting options that should be applied. (Optional.)</param> /// <returns>A string representation of this instance.</returns> public static string GetFormattedName(this Type type, TypeNameFormatOptions options = TypeNameFormatOptions.Default) { var stringBuilder = new StringBuilder(); stringBuilder.AppendFormattedName(type, options); return(stringBuilder.ToString()); }
private static void AppendFormattedName(this StringBuilder stringBuilder, Type type, TypeNameFormatOptions options, Type typeWithGenericTypeArgs) { // PHASE 1: Rule out several special cases. (These are mostly "composite" types requiring some recursion.) // Types for which there is a keyword: if (IsSet(TypeNameFormatOptions.NoKeywords, options) == false && typeKeywords.TryGetValue(type, out string typeKeyword)) { stringBuilder.Append(typeKeyword); return; } // Arrays, by-ref, and pointer types: if (type.HasElementType) { var elementType = type.GetElementType(); if (type.IsArray) { var ranks = new Queue <int>(); ranks.Enqueue(type.GetArrayRank()); HandleArrayElementType(elementType, ranks); void HandleArrayElementType(Type et, Queue <int> r) { if (et.IsArray) { r.Enqueue(et.GetArrayRank()); HandleArrayElementType(et.GetElementType(), r); } else { stringBuilder.AppendFormattedName(et, options); while (r.Count > 0) { stringBuilder.Append('['); stringBuilder.Append(',', r.Dequeue() - 1); stringBuilder.Append(']'); } } } } else if (type.IsByRef) { stringBuilder.Append("ref "); stringBuilder.AppendFormattedName(elementType, options); } else { Debug.Assert(type.IsPointer, "Only array, by-ref, and pointer types have an element type."); stringBuilder.AppendFormattedName(elementType, options); stringBuilder.Append('*'); } return; } var isConstructedGenericType = IsConstructedGenericType(typeWithGenericTypeArgs); if (isConstructedGenericType) { // Nullable value types (excluding the open generic Nullable<T> itself): if (IsSet(TypeNameFormatOptions.NoNullableQuestionMark, options) == false) { var nullableArg = Nullable.GetUnderlyingType(type); if (nullableArg != null) { stringBuilder.AppendFormattedName(nullableArg, options); stringBuilder.Append('?'); return; } } // Value tuple types (any type called System.ValueTuple`n): if (IsSet(TypeNameFormatOptions.NoTuple, options) == false && type.Name.StartsWith("ValueTuple`", StringComparison.Ordinal) && type.Namespace == "System") { var genericTypeArgs = GetGenericTypeArguments(typeWithGenericTypeArgs); stringBuilder.Append('('); for (int i = 0, n = genericTypeArgs.Length; i < n; ++i) { if (i > 0) { stringBuilder.Append(", "); } stringBuilder.AppendFormattedName(genericTypeArgs[i], options); } stringBuilder.Append(')'); return; } } var name = type.Name; if (IsSet(TypeNameFormatOptions.NoAnonymousTypes, options) == false && name.StartsWith("<>f", StringComparison.Ordinal)) { stringBuilder.Append('{'); int i = 0; foreach (var property in GetDeclaredProperties(type)) { if (i > 0) { stringBuilder.Append(", "); } stringBuilder.AppendFormattedName(property.PropertyType, options) .Append(' ') .Append(property.Name); ++i; } stringBuilder.Append('}'); return; } // PHASE 2: Format a (non-"composite") type's name. // Possible prefix (namespace or name of enclosing type): if (!type.IsGenericParameter) { if (type.IsNested) { stringBuilder.AppendFormattedName(type.DeclaringType, options, typeWithGenericTypeArgs); stringBuilder.Append('.'); } else if (IsSet(TypeNameFormatOptions.Namespaces, options)) { string @namespace = type.Namespace; if (string.IsNullOrEmpty(@namespace) == false) { stringBuilder.Append(type.Namespace); stringBuilder.Append('.'); } } } // Actual type name, optionally followed by a generic parameter/argument list: if (isConstructedGenericType || IsGenericType(type)) { var backtickIndex = name.LastIndexOf('`'); if (backtickIndex >= 0) { stringBuilder.Append(name, 0, backtickIndex); } else { stringBuilder.Append(name); } var ownGenericTypeParamCount = GetGenericTypeArguments(type).Length; int ownGenericTypeArgStartIndex = 0; if (type.IsNested) { var outerTypeGenericTypeParamCount = GetGenericTypeArguments(type.DeclaringType).Length; if (ownGenericTypeParamCount >= outerTypeGenericTypeParamCount) { ownGenericTypeArgStartIndex = outerTypeGenericTypeParamCount; } } if (ownGenericTypeArgStartIndex < ownGenericTypeParamCount) { stringBuilder.Append('<'); if (isConstructedGenericType || IsSet(TypeNameFormatOptions.NoGenericParameterNames, options) == false) { var genericTypeArgs = GetGenericTypeArguments(typeWithGenericTypeArgs); for (int i = ownGenericTypeArgStartIndex, n = ownGenericTypeParamCount; i < n; ++i) { if (i > ownGenericTypeArgStartIndex) { stringBuilder.Append(", "); } stringBuilder.AppendFormattedName(genericTypeArgs[i], options); } } else { stringBuilder.Append(',', ownGenericTypeParamCount - ownGenericTypeArgStartIndex - 1); } stringBuilder.Append('>'); } } else { stringBuilder.Append(name); } }
/// <summary> /// Appends a string representation of the specified type to this instance. /// </summary> /// <param name="stringBuilder">The <see cref="StringBuilder"/> instance to which to append.</param> /// <param name="type">The <see cref="Type"/> of which a string representation should be appended.</param> /// <param name="options">Any combination of formatting options that should be applied. (Optional.)</param> /// <returns>A reference to this instance after the append operation has completed.</returns> public static StringBuilder AppendFormattedName(this StringBuilder stringBuilder, Type type, TypeNameFormatOptions options = TypeNameFormatOptions.Default) { stringBuilder.AppendFormattedName(type, options, type); return(stringBuilder); }
private static void WriteNamePart(TypeInfo type, StringBuilder builder, ArraySegment <TypeInfo> genericArguments, TypeNameFormatOptions options) { if (type == null) { throw new ArgumentNullException("type"); } if (builder == null) { throw new ArgumentNullException("builder"); } builder.Append(type.Name); if (genericArguments.Count > 0) { builder.Append("<"); for (var i = genericArguments.Offset; i < genericArguments.Offset + genericArguments.Count; i++) { // ReSharper disable once PossibleNullReferenceException if (genericArguments.Array[i].IsGenericParameter == false) { WriteName(genericArguments.Array[i], builder, options); } builder.Append(','); } builder.Length--; builder.Append(">"); } }
/// <remarks> /// Replacement for <see cref="M:System.Enum.HasFlag(System.Enum)"/> /// which may be slow or even unavailable on earlier target frameworks. /// </remarks> private static bool IsSet(TypeNameFormatOptions option, TypeNameFormatOptions options) { return((options & option) == option); }
public static StringBuilder GetCSharpFullName(this Type type, StringBuilder builder = null, TypeNameFormatOptions options = TypeNameFormatOptions.IncludeGenericSuffix) { if (type == null) { throw new ArgumentNullException("type"); } return(GetCSharpFullName(type.GetTypeInfo(), builder, options)); }
private static void WriteName(TypeInfo typeInfo, StringBuilder builder, TypeNameFormatOptions options) { if (typeInfo == null) { throw new ArgumentNullException("typeInfo"); } if (builder == null) { throw new ArgumentNullException("builder"); } var alias = default(string); if ((options & TypeNameFormatOptions.UseAliases) == TypeNameFormatOptions.UseAliases && CSharpTypeNameAlias.TryGetAlias(typeInfo, out alias)) { builder.Append(alias); return; } var arrayDepth = 0; while (typeInfo.IsArray) { typeInfo = typeInfo.GetElementType().GetTypeInfo(); arrayDepth++; } var writeGenericArguments = (options & TypeNameFormatOptions.IncludeGenericArguments) == TypeNameFormatOptions.IncludeGenericArguments; var namespaceWritten = (options & TypeNameFormatOptions.IncludeNamespace) != TypeNameFormatOptions.IncludeNamespace; var writeDeclaringType = (options & TypeNameFormatOptions.IncludeDeclaringType) == TypeNameFormatOptions.IncludeDeclaringType; var genericArguments = (typeInfo.IsGenericType && writeGenericArguments ? typeInfo.GetGenericArguments() : Type.EmptyTypes).ConvertAll(t => t.GetTypeInfo()); var genericArgumentOffset = 0; foreach (var declaringTypeInfo in new TypeNestingEnumerator(typeInfo)) { if (!namespaceWritten) { var typeNamespace = declaringTypeInfo.Namespace; builder.Append(typeNamespace); if (!string.IsNullOrEmpty(typeNamespace)) { builder.Append('.'); } namespaceWritten = true; } var genericArgumentsCount = (declaringTypeInfo.IsGenericType && writeGenericArguments ? declaringTypeInfo.GetGenericArguments().Length : 0) - genericArgumentOffset; var partialGenerics = new ArraySegment <TypeInfo>(genericArguments, genericArgumentOffset, genericArgumentsCount); if (writeDeclaringType || declaringTypeInfo.Equals(typeInfo)) { WriteNamePart(declaringTypeInfo, builder, partialGenerics, options); if (declaringTypeInfo.Equals(typeInfo) == false) { builder.Append('.'); } } genericArgumentOffset += genericArgumentsCount; } for (var d = 0; d < arrayDepth; d++) { builder.Append("[]"); } }
public static StringBuilder GetCSharpNameOnly(this TypeInfo typeInfo, StringBuilder builder = null, TypeNameFormatOptions options = TypeNameFormatOptions.IncludeGenericSuffix) { if (typeInfo == null) { throw new ArgumentNullException("typeInfo"); } if (builder == null) { builder = new StringBuilder(); } var nameStartIndex = builder.Length; WriteName(typeInfo, builder, options & ~TypeNameFormatOptions.IncludeNamespace); if ((options & TypeNameFormatOptions.IncludeGenericSuffix) == 0) { RemoveGenericSuffix(builder, nameStartIndex, builder.Length - nameStartIndex); } return(builder); }
public void Anonymous_type(string expectedFormattedName, object anonymouslyTypedObj, TypeNameFormatOptions options) { Assert.Equal(expectedFormattedName, anonymouslyTypedObj.GetType().GetFormattedName(options)); }
public void Generic_type_parameter_acting_as_argument_in_constructed_type(string expectedFormattedName, Type type, TypeNameFormatOptions options) { Assert.Equal(expectedFormattedName, type.GetFormattedName(options)); }
public void Value_tuple(string expectedFormattedName, Type type, TypeNameFormatOptions options) { Assert.Equal(expectedFormattedName, type.GetFormattedName(options)); }
public void Nested_generic_type_instantiation(string expectedFormattedName, Type type, TypeNameFormatOptions options) { Assert.Equal(expectedFormattedName, type.GetFormattedName(options)); }