< Summary

Line coverage
0%
Covered lines: 0
Uncovered lines: 277
Coverable lines: 277
Total lines: 687
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 106
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)0%440%
.ctor(...)100%110%
AppendFullName(...)0%18180%
AppendName(...)0%14140%
GetNodeCount()0%12120%
GetElementType()0%660%
GetGenericTypeDefinition()0%220%
Parse(...)100%110%
TryParse(...)100%110%
Unescape(...)0%220%
GetArrayRank()0%660%
GetGenericArguments()100%110%
WithAssemblyName(...)0%440%
MakeSZArrayTypeName()100%110%
MakeArrayTypeName(...)0%220%
MakePointerTypeName()100%110%
MakeByRefTypeName()100%110%
MakeGenericTypeName(...)0%220%
MakeElementTypeName(...)100%110%

File(s)

https://raw.githubusercontent.com/dotnet/runtime/811a7eabb75c42db53440e8ba3f60c07511cfd1f/src/libraries/System.Reflection.Metadata/src/System/Reflection/Metadata/TypeName.cs

#LineLine coverage
 1// Licensed to the .NET Foundation under one or more agreements.
 2// The .NET Foundation licenses this file to you under the MIT license.
 3
 4using System.Collections.Generic;
 5using System.Diagnostics;
 6using System.Diagnostics.CodeAnalysis;
 7using System.Runtime.InteropServices;
 8using System.Text;
 9
 10#if !SYSTEM_PRIVATE_CORELIB
 11using System.Collections.Immutable;
 12#endif
 13
 14namespace System.Reflection.Metadata
 15{
 16    [DebuggerDisplay("{AssemblyQualifiedName}")]
 17#if SYSTEM_REFLECTION_METADATA
 18    public
 19#else
 20    internal
 21#endif
 22    sealed class TypeName
 23    {
 24        /// <summary>
 25        /// Positive value is array rank.
 26        /// Negative value is modifier encoded using constants defined in <see cref="TypeNameParserHelpers"/>.
 27        /// </summary>
 28        private readonly int _rankOrModifier;
 29        /// <summary>
 30        /// To avoid the need of allocating a string for all declaring types (example: A+B+C+D+E+F+G),
 31        /// length of the name is stored and the fullName passed in ctor represents the full name of the nested type.
 32        /// So when the name is needed, a substring is being performed.
 33        /// </summary>
 34        private readonly int _nestedNameLength;
 35        private readonly TypeName? _elementOrGenericType;
 36        private readonly TypeName? _declaringType;
 37#if SYSTEM_PRIVATE_CORELIB
 38        private readonly List<TypeName>? _genericArguments;
 39#else
 40        private readonly ImmutableArray<TypeName> _genericArguments;
 41#endif
 42        private string? _name, _namespace, _fullName, _assemblyQualifiedName;
 43
 044        internal TypeName(string? fullName,
 045            AssemblyNameInfo? assemblyName,
 046            TypeName? elementOrGenericType = default,
 047            TypeName? declaringType = default,
 048#if SYSTEM_PRIVATE_CORELIB
 049            List<TypeName>? genericTypeArguments = default,
 050#else
 051            ImmutableArray<TypeName>.Builder? genericTypeArguments = default,
 052#endif
 053            int rankOrModifier = default,
 054            int nestedNameLength = -1)
 055        {
 056            _fullName = fullName;
 057            AssemblyName = assemblyName;
 058            _rankOrModifier = rankOrModifier;
 059            _elementOrGenericType = elementOrGenericType;
 060            _declaringType = declaringType;
 061            _nestedNameLength = nestedNameLength;
 62
 63#if SYSTEM_PRIVATE_CORELIB
 64            _genericArguments = genericTypeArguments;
 65#else
 066            _genericArguments = genericTypeArguments is null
 067                ? ImmutableArray<TypeName>.Empty
 068                : genericTypeArguments.Count == genericTypeArguments.Capacity ? genericTypeArguments.MoveToImmutable() :
 69#endif
 070        }
 71
 72#if SYSTEM_REFLECTION_METADATA
 073        private TypeName(string? fullName,
 074            AssemblyNameInfo? assemblyName,
 075            TypeName? elementOrGenericType,
 076            TypeName? declaringType,
 077            ImmutableArray<TypeName> genericTypeArguments,
 078            int rankOrModifier = default,
 079            int nestedNameLength = -1)
 080        {
 081            _fullName = fullName;
 082            AssemblyName = assemblyName;
 083            _elementOrGenericType = elementOrGenericType;
 084            _declaringType = declaringType;
 085            _genericArguments = genericTypeArguments;
 086            _rankOrModifier = rankOrModifier;
 087            _nestedNameLength = nestedNameLength;
 088        }
 89#endif
 90
 91        /// <summary>
 92        /// The assembly-qualified name of the type; e.g., "System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, Pu
 93        /// </summary>
 94        /// <remarks>
 95        /// If <see cref="AssemblyName"/> returns null, simply returns <see cref="FullName"/>.
 96        /// </remarks>
 97        public string AssemblyQualifiedName
 98        {
 99            get
 0100            {
 0101                if (_assemblyQualifiedName is null)
 0102                {
 0103                    if (_fullName is not null && AssemblyName is null)
 0104                    {
 105                        // _fullName may carry more information than FullName property, so we need to use FullName prope
 0106                        _assemblyQualifiedName = FullName;
 0107                    }
 108                    else
 0109                    {
 0110                        ValueStringBuilder builder = new(stackalloc char[256]);
 0111                        AppendFullName(ref builder); // see recursion comments in AppendFullName
 0112                        if (AssemblyName is not null)
 0113                        {
 0114                            builder.Append(", ");
 0115                            AssemblyName.AppendFullName(ref builder);
 0116                        }
 0117                        _assemblyQualifiedName = builder.ToString();
 118
 0119                        if (AssemblyName is null)
 0120                        {
 121                            // If the type name was not created from a assembly-qualified name,
 122                            // the FullName and AssemblyQualifiedName are the same.
 0123                            _fullName = _assemblyQualifiedName;
 0124                        }
 0125                    }
 0126                }
 127
 0128                return _assemblyQualifiedName;
 0129            }
 130        }
 131
 132        /// <summary>
 133        /// Returns assembly name which contains this type, or null if this <see cref="TypeName"/> was not
 134        /// created from a fully-qualified name.
 135        /// </summary>
 0136        public AssemblyNameInfo? AssemblyName { get; }
 137
 138        /// <summary>
 139        /// If this type is a nested type (see <see cref="IsNested"/>), gets
 140        /// the declaring type. If this type is not a nested type, throws.
 141        /// </summary>
 142        /// <remarks>
 143        /// For example, given "Namespace.Declaring+Nested", unwraps the outermost type and returns "Namespace.Declaring
 144        /// </remarks>
 145        /// <exception cref="InvalidOperationException">The current type is not a nested type.</exception>
 146        public TypeName DeclaringType
 147        {
 148            get
 0149            {
 0150                if (_declaringType is null)
 0151                {
 0152                    TypeNameParserHelpers.ThrowInvalidOperation_NotNestedType();
 153                }
 154
 0155                return _declaringType;
 0156            }
 157        }
 158
 159        /// <summary>
 160        /// The full name of this type, including namespace, but without the assembly name; e.g., "System.Int32".
 161        /// Nested types are represented with a '+'; e.g., "MyNamespace.MyType+NestedType".
 162        /// </summary>
 163        /// <remarks>
 164        /// <para>For constructed generic types, the type arguments will be listed using their fully qualified
 165        /// names. For example, given "List&lt;int&gt;", the <see cref="FullName"/> property will return
 166        /// "System.Collections.Generic.List`1[[System.Int32, mscorlib, ...]]".</para>
 167        /// <para>For open generic types, the convention is to use a backtick ("`") followed by
 168        /// the arity of the generic type. For example, given "Dictionary&lt;,&gt;", the <see cref="FullName"/>
 169        /// property will return "System.Collections.Generic.Dictionary`2". Given "Dictionary&lt;,&gt;.Enumerator",
 170        /// the <see cref="FullName"/> property will return "System.Collections.Generic.Dictionary`2+Enumerator".
 171        /// See ECMA-335, Sec. I.10.7.2 (Type names and arity encoding) for more information.</para>
 172        /// </remarks>
 173        public string FullName
 174        {
 175            get
 0176            {
 0177                if (_fullName is null)
 0178                {
 0179                    ValueStringBuilder builder = new(stackalloc char[128]);
 0180                    AppendFullName(ref builder);
 0181                    _fullName = builder.ToString();
 0182                }
 0183                else if (_nestedNameLength > 0 && _fullName.Length > _nestedNameLength) // Declaring types
 0184                {
 185                    // Stored fullName represents the full name of the nested type.
 186                    // Example: Namespace.Declaring+Nested
 0187                    _fullName = _fullName.Substring(0, _nestedNameLength);
 0188                }
 189
 0190                return _fullName!;
 0191            }
 192        }
 193
 194        private void AppendFullName(ref ValueStringBuilder builder)
 0195        {
 196            // This is a recursive method over potentially hostile input. Protection against DoS is offered
 197            // via the [Try]Parse method and TypeNameParseOptions.MaxNodes property at construction time.
 198            // This FullName property getter and related methods assume that this TypeName instance has an
 199            // acceptable node count.
 200            //
 201            // The node count controls the total amount of work performed by this method, including:
 202            // - The max possible stack depth due to the recursive methods calls.
 203
 0204            if (_fullName is null)
 0205            {
 0206                if (IsConstructedGenericType)
 0207                {
 0208                    GetGenericTypeDefinition().AppendFullName(ref builder);
 0209                    builder.Append('[');
 0210                    foreach (TypeName genericArg in GetGenericArguments())
 0211                    {
 0212                        builder.Append('[');
 0213                        genericArg.AppendFullName(ref builder);
 214                        // Generic arguments need to be always fully qualified.
 0215                        if (genericArg.AssemblyName is not null)
 0216                        {
 0217                            builder.Append(", ");
 0218                            genericArg.AssemblyName.AppendFullName(ref builder);
 0219                        }
 0220                        builder.Append("],");
 0221                    }
 0222                    builder[builder.Length - 1] = ']'; // replace ',' with ']'
 0223                }
 0224                else if (IsArray || IsPointer || IsByRef)
 0225                {
 0226                    GetElementType().AppendFullName(ref builder);
 0227                    TypeNameParserHelpers.AppendRankOrModifierStringRepresentation(_rankOrModifier, ref builder);
 0228                }
 229                else
 0230                {
 0231                    Debug.Fail("Pre-allocated full name should have been provided in the ctor");
 232                }
 0233            }
 0234            else if (_nestedNameLength > 0 && _fullName.Length > _nestedNameLength) // Declaring types
 0235            {
 236                // Stored fullName represents the full name of the nested type.
 237                // Example: Namespace.Declaring+Nested
 0238                builder.Append(_fullName.AsSpan(0, _nestedNameLength));
 0239            }
 240            else
 0241            {
 0242                builder.Append(_fullName);
 0243            }
 0244        }
 245
 246        /// <summary>
 247        /// Returns true if this type represents any kind of array, regardless of the array's
 248        /// rank or its bounds.
 249        /// </summary>
 0250        public bool IsArray => _rankOrModifier == TypeNameParserHelpers.SZArray || _rankOrModifier > 0;
 251
 252        /// <summary>
 253        /// Returns true if this type represents a constructed generic type (e.g., "List&lt;int&gt;").
 254        /// </summary>
 255        /// <remarks>
 256        /// Returns false for open generic types (e.g., "Dictionary&lt;,&gt;").
 257        /// </remarks>
 258        public bool IsConstructedGenericType =>
 259#if SYSTEM_PRIVATE_CORELIB
 260            _genericArguments is not null;
 261#else
 0262            _genericArguments.Length > 0;
 263#endif
 264
 265        /// <summary>
 266        /// Returns true if this is a "plain" type; that is, not an array, not a pointer, not a reference, and
 267        /// not a constructed generic type. Examples of elemental types are "System.Int32",
 268        /// "System.Uri", and "YourNamespace.YourClass".
 269        /// </summary>
 270        /// <remarks>
 271        /// <para>This property returning true doesn't mean that the type is a primitive like string
 272        /// or int; it just means that there's no underlying type.</para>
 273        /// <para>This property will return true for generic type definitions (e.g., "Dictionary&lt;,&gt;").
 274        /// This is because determining whether a type truly is a generic type requires loading the type
 275        /// and performing a runtime check.</para>
 276        /// </remarks>
 277        [MemberNotNullWhen(false, nameof(_elementOrGenericType))]
 0278        public bool IsSimple => _elementOrGenericType is null;
 279
 280        /// <summary>
 281        /// Returns true if this is a managed pointer type (e.g., "ref int").
 282        /// Managed pointer types are sometimes called byref types (<seealso cref="Type.IsByRef"/>)
 283        /// </summary>
 0284        public bool IsByRef => _rankOrModifier == TypeNameParserHelpers.ByRef;
 285
 286        /// <summary>
 287        /// Returns true if this is a nested type (e.g., "Namespace.Declaring+Nested").
 288        /// For nested types <seealso cref="DeclaringType"/> returns their declaring type.
 289        /// </summary>
 290        [MemberNotNullWhen(true, nameof(_declaringType))]
 0291        public bool IsNested => _declaringType is not null;
 292
 293        /// <summary>
 294        /// Returns true if this type represents a single-dimensional, zero-indexed array (e.g., "int[]").
 295        /// </summary>
 0296        public bool IsSZArray => _rankOrModifier == TypeNameParserHelpers.SZArray;
 297
 298        /// <summary>
 299        /// Returns true if this type represents an unmanaged pointer (e.g., "int*" or "void*").
 300        /// Unmanaged pointer types are often just called pointers (<seealso cref="Type.IsPointer"/>)
 301        /// </summary>
 0302        public bool IsPointer => _rankOrModifier == TypeNameParserHelpers.Pointer;
 303
 304        /// <summary>
 305        /// Returns true if this type represents a variable-bound array; that is, an array of rank greater
 306        /// than 1 (e.g., "int[,]") or a single-dimensional array which isn't necessarily zero-indexed.
 307        /// </summary>
 0308        public bool IsVariableBoundArrayType => _rankOrModifier >= 1;
 309
 310        /// <summary>
 311        /// The name of this type, without the namespace and the assembly name; e.g., "Int32".
 312        /// Nested types are represented without a '+'; e.g., "MyNamespace.MyType+NestedType" is just "NestedType".
 313        /// </summary>
 314        public string Name
 315        {
 316            get
 0317            {
 0318                if (_name is null)
 0319                {
 0320                    ValueStringBuilder builder = new(stackalloc char[64]);
 0321                    AppendName(ref builder);
 0322                    _name = builder.ToString();
 0323                }
 324
 0325                return _name;
 0326            }
 327        }
 328
 329        private void AppendName(ref ValueStringBuilder builder)
 0330        {
 331            // Lookups to Name and FullName might be recursive. See comments in AppendFullName method.
 332
 0333            if (IsConstructedGenericType)
 0334            {
 0335                GetGenericTypeDefinition().AppendName(ref builder);
 0336            }
 0337            else if (IsPointer || IsByRef || IsArray)
 0338            {
 0339                GetElementType().AppendName(ref builder);
 0340                TypeNameParserHelpers.AppendRankOrModifierStringRepresentation(_rankOrModifier, ref builder);
 0341            }
 342            else
 0343            {
 344                // _fullName can be null only in constructed generic or modified types, which we handled above.
 0345                Debug.Assert(_fullName is not null);
 0346                ReadOnlySpan<char> name = _fullName.AsSpan();
 0347                if (_nestedNameLength > 0)
 0348                {
 0349                    name = name.Slice(0, _nestedNameLength);
 0350                }
 0351                if (IsNested)
 0352                {
 353                    // If the type is nested, we know the length of the declaring type's full name.
 354                    // Get the characters after that plus one for the '+' separator.
 0355                    name = name.Slice(_declaringType._nestedNameLength + 1);
 0356                }
 0357                else if (TypeNameParserHelpers.IndexOfNamespaceDelimiter(name) is int idx && idx >= 0)
 0358                {
 359                    // If the type is not nested, find the namespace delimiter in the full name and return the substring
 0360                    name = name.Slice(idx + 1);
 0361                }
 0362                builder.Append(name);
 0363            }
 0364        }
 365
 366        /// <summary>
 367        /// The namespace of this type; e.g., "System".
 368        /// </summary>
 369        /// <exception cref="InvalidOperationException">This instance is a nested type.</exception>
 370        public string Namespace
 371        {
 372            get
 0373            {
 0374                if (_namespace is null)
 0375                {
 0376                    TypeName rootTypeName = this;
 0377                    while (!rootTypeName.IsSimple)
 0378                    {
 0379                        rootTypeName = rootTypeName._elementOrGenericType;
 0380                    }
 381
 0382                    if (rootTypeName.IsNested)
 0383                    {
 0384                        TypeNameParserHelpers.ThrowInvalidOperation_NestedTypeNamespace();
 385                    }
 386
 387                    // By setting the namespace field at the root type name, we avoid recomputing it for all derived nam
 0388                    if (rootTypeName._namespace is null)
 0389                    {
 390                        // At this point the type does not have a modifier applied to it, so it should have its full nam
 0391                        Debug.Assert(rootTypeName._fullName is not null);
 0392                        ReadOnlySpan<char> rootFullName = rootTypeName._fullName.AsSpan();
 0393                        if (rootTypeName._nestedNameLength > 0)
 0394                        {
 0395                            rootFullName = rootFullName.Slice(0, rootTypeName._nestedNameLength);
 0396                        }
 0397                        if (TypeNameParserHelpers.IndexOfNamespaceDelimiter(rootFullName) is int idx && idx >= 0)
 0398                        {
 0399                            rootTypeName._namespace = rootFullName.Slice(0, idx).ToString();
 0400                        }
 401                        else
 0402                        {
 0403                            rootTypeName._namespace = string.Empty;
 0404                        }
 0405                    }
 0406                    _namespace = rootTypeName._namespace;
 0407                }
 408
 0409                return _namespace;
 0410            }
 411        }
 412
 413        /// <summary>
 414        /// Represents the total number of <see cref="TypeName"/> instances that are used to describe
 415        /// this instance, including any generic arguments or underlying types.
 416        /// </summary>
 417        /// <remarks>
 418        /// <para>This value is computed every time this method gets called, it's not cached.</para>
 419        /// <para>There's not really a parallel concept to this in reflection. Think of it
 420        /// as the total number of <see cref="TypeName"/> instances that would be created if
 421        /// you were to totally deconstruct this instance and visit each intermediate <see cref="TypeName"/>
 422        /// that occurs as part of deconstruction.</para>
 423        /// <para>"int" and "Person" each have complexities of 1 because they're standalone types.</para>
 424        /// <para>"int[]" has a node count of 2 because to fully inspect it involves inspecting the
 425        /// array type itself, <em>plus</em> unwrapping the underlying type ("int") and inspecting that.</para>
 426        /// <para>
 427        /// "Dictionary&lt;string, List&lt;int[][]&gt;&gt;" has node count 8 because fully visiting it
 428        /// involves inspecting 8 <see cref="TypeName"/> instances total:
 429        /// <list type="bullet">
 430        /// <item>Dictionary&lt;string, List&lt;int[][]&gt;&gt; (the original type)</item>
 431        /// <item>Dictionary`2 (the generic type definition)</item>
 432        /// <item>string (a type argument of Dictionary)</item>
 433        /// <item>List&lt;int[][]&gt; (a type argument of Dictionary)</item>
 434        /// <item>List`1 (the generic type definition)</item>
 435        /// <item>int[][] (a type argument of List)</item>
 436        /// <item>int[] (the underlying type of int[][])</item>
 437        /// <item>int (the underlying type of int[])</item>
 438        /// </list>
 439        /// </para>
 440        /// </remarks>
 441        /// <exception cref="OverflowException">The total number of <see cref="TypeName"/> instances that are used to de
 442        /// this instance exceed <see cref="int.MaxValue"/>.</exception>
 443        public int GetNodeCount()
 0444        {
 445            // This method uses checked arithmetic to avoid silent overflows.
 446            // It's impossible to parse a TypeName with NodeCount > int.MaxValue
 447            // (TypeNameParseOptions.MaxNodes is an int), but it's possible
 448            // to create such names with the Make* APIs.
 0449            int result = 1;
 450
 0451            if (IsArray || IsPointer || IsByRef)
 0452            {
 0453                result = checked(result + GetElementType().GetNodeCount());
 0454            }
 0455            else if (IsConstructedGenericType)
 0456            {
 0457                result = checked(result + GetGenericTypeDefinition().GetNodeCount());
 458
 0459                foreach (TypeName genericArgument in GetGenericArguments())
 0460                {
 0461                    result = checked(result + genericArgument.GetNodeCount());
 0462                }
 0463            }
 0464            else if (IsNested)
 0465            {
 0466                result = checked(result + DeclaringType.GetNodeCount());
 0467            }
 468
 0469            return result;
 0470        }
 471
 472        /// <summary>
 473        /// The TypeName of the object encompassed or referred to by the current array, pointer, or reference type.
 474        /// </summary>
 475        /// <remarks>
 476        /// For example, given "int[][]", unwraps the outermost array and returns "int[]".
 477        /// </remarks>
 478        /// <exception cref="InvalidOperationException">The current type is not an array, pointer or reference.</excepti
 479        public TypeName GetElementType()
 0480        {
 0481            if (!(IsArray || IsPointer || IsByRef))
 0482            {
 0483                TypeNameParserHelpers.ThrowInvalidOperation_NoElement();
 484            }
 485
 0486            return _elementOrGenericType!;
 0487        }
 488
 489        /// <summary>
 490        /// Returns a TypeName object that represents a generic type name definition from which the current generic type
 491        /// </summary>
 492        /// <remarks>
 493        /// Given "Dictionary&lt;string, int&gt;", returns the generic type definition "Dictionary&lt;,&gt;".
 494        /// </remarks>
 495        /// <exception cref="InvalidOperationException">The current type is not a generic type.</exception>
 496        public TypeName GetGenericTypeDefinition()
 0497        {
 0498            if (!IsConstructedGenericType)
 0499            {
 0500                TypeNameParserHelpers.ThrowInvalidOperation_NotGenericType();
 501            }
 502
 0503            return _elementOrGenericType!;
 0504        }
 505
 506        /// <summary>
 507        /// Parses a span of characters into a type name.
 508        /// </summary>
 509        /// <param name="typeName">A span containing the characters representing the type name to parse.</param>
 510        /// <param name="options">An object that describes optional <seealso cref="TypeNameParseOptions"/> parameters to
 511        /// <returns>Parsed type name.</returns>
 512        /// <exception cref="ArgumentException">Provided type name was invalid.</exception>
 513        /// <exception cref="InvalidOperationException">Parsing has exceeded the limit set by <seealso cref="TypeNamePar
 514        public static TypeName Parse(ReadOnlySpan<char> typeName, TypeNameParseOptions? options = default)
 0515            => TypeNameParser.Parse(typeName, throwOnError: true, options)!;
 516
 517        /// <summary>
 518        /// Tries to parse a span of characters into a type name.
 519        /// </summary>
 520        /// <param name="typeName">A span containing the characters representing the type name to parse.</param>
 521        /// <param name="options">An object that describes optional <seealso cref="TypeNameParseOptions"/> parameters to
 522        /// <param name="result">Contains the result when parsing succeeds.</param>
 523        /// <returns>true if type name was converted successfully, otherwise, false.</returns>
 524        public static bool TryParse(ReadOnlySpan<char> typeName, [NotNullWhen(true)] out TypeName? result, TypeNameParse
 0525        {
 0526            result = TypeNameParser.Parse(typeName, throwOnError: false, options);
 0527            return result is not null;
 0528        }
 529
 530        /// <summary>
 531        /// Converts any escaped characters in the input type name or namespace.
 532        /// </summary>
 533        /// <param name="name">The input string containing the name to convert.</param>
 534        /// <returns>A string of characters with any escaped characters converted to their unescaped form.</returns>
 535        /// <remarks>
 536        /// <para>The unescaped string can be used for looking up the type name or namespace in metadata.</para>
 537        /// <para>This method removes escape characters even if they precede a character that does not require escaping.
 538        /// </remarks>
 539        public static string Unescape(string name)
 0540        {
 0541            if (name is null)
 0542            {
 0543                TypeNameParserHelpers.ThrowArgumentNullException(nameof(name));
 544            }
 545
 0546            return TypeNameParserHelpers.Unescape(name);
 0547        }
 548
 549        /// <summary>
 550        /// Gets the number of dimensions in an array.
 551        /// </summary>
 552        /// <returns>An integer that contains the number of dimensions in the current type.</returns>
 553        /// <exception cref="InvalidOperationException">The current type is not an array.</exception>
 554        public int GetArrayRank()
 0555        {
 0556            if (!(_rankOrModifier == TypeNameParserHelpers.SZArray || _rankOrModifier > 0))
 0557            {
 0558                TypeNameParserHelpers.ThrowInvalidOperation_HasToBeArrayClass();
 559            }
 560
 0561            return _rankOrModifier == TypeNameParserHelpers.SZArray ? 1 : _rankOrModifier;
 0562        }
 563
 564        /// <summary>
 565        /// If this <see cref="TypeName"/> represents a constructed generic type, returns an array
 566        /// of all the generic arguments. Otherwise it returns an empty array.
 567        /// </summary>
 568        /// <remarks>
 569        /// <para>For example, given "Dictionary&lt;string, int&gt;", returns a 2-element array containing
 570        /// string and int.</para>
 571        /// </remarks>
 572        public
 573#if SYSTEM_PRIVATE_CORELIB
 574        ReadOnlySpan<TypeName> GetGenericArguments() => CollectionsMarshal.AsSpan(_genericArguments);
 575#else
 0576        ImmutableArray<TypeName> GetGenericArguments() => _genericArguments;
 577#endif
 578
 579#if SYSTEM_REFLECTION_METADATA
 580        /// <summary>
 581        /// Creates a new <see cref="TypeName" /> object that represents current simple name with provided assembly name
 582        /// </summary>
 583        /// <param name="assemblyName">Assembly name.</param>
 584        /// <returns>Created simple name.</returns>
 585        /// <exception cref="InvalidOperationException">The current type name is not simple.</exception>
 586        public TypeName WithAssemblyName(AssemblyNameInfo? assemblyName)
 0587        {
 588            // Recursive method. See comments in FullName property getter for more information
 589            // on how this is protected against attack.
 590            //
 591            // n.b. AssemblyNameInfo could also be hostile. The typical exploit is that a single
 592            // long AssemblyNameInfo is associated with one or more simple TypeName objects,
 593            // leading to an alg. complexity attack (DoS). It's important that TypeName doesn't
 594            // actually *do* anything with the provided AssemblyNameInfo rather than store it.
 595            // For example, don't use it inside a string concat operation unless the caller
 596            // explicitly requested that to happen. If the input is hostile, the caller should
 597            // never perform such concats in a loop.
 598
 0599            if (!IsSimple)
 0600            {
 0601                TypeNameParserHelpers.ThrowInvalidOperation_NotSimpleName(FullName);
 602            }
 603
 0604            TypeName? declaringType = IsNested
 0605                ? DeclaringType.WithAssemblyName(assemblyName)
 0606                : null;
 607
 0608            return new TypeName(fullName: _fullName,
 0609                assemblyName: assemblyName,
 0610                elementOrGenericType: null,
 0611                declaringType: declaringType,
 0612                genericTypeArguments: ImmutableArray<TypeName>.Empty,
 0613                nestedNameLength: _nestedNameLength);
 0614        }
 615
 616        /// <summary>
 617        /// Creates a <see cref="TypeName" /> object representing a one-dimensional array
 618        /// of the current type, with a lower bound of zero.
 619        /// </summary>
 620        /// <returns>
 621        /// A <see cref="TypeName" /> object representing a one-dimensional array
 622        /// of the current type, with a lower bound of zero.
 623        /// </returns>
 0624        public TypeName MakeSZArrayTypeName() => MakeElementTypeName(TypeNameParserHelpers.SZArray);
 625
 626        /// <summary>
 627        /// Creates a <see cref="TypeName" /> object representing an array of the current type,
 628        /// with the specified number of dimensions.
 629        /// </summary>
 630        /// <param name="rank">The number of dimensions for the array. This number must be more than zero and less than 
 631        /// <returns>
 632        /// A <see cref="TypeName" /> object representing an array of the current type,
 633        /// with the specified number of dimensions.
 634        /// </returns>
 635        /// <exception cref="ArgumentOutOfRangeException">rank is invalid. For example, 0 or negative.</exception>
 636        public TypeName MakeArrayTypeName(int rank)
 0637            => rank <= 0
 0638                ? throw new ArgumentOutOfRangeException(nameof(rank))
 0639                : MakeElementTypeName(rank);
 640
 641        /// <summary>
 642        /// Creates a <see cref="TypeName" /> object that represents a pointer to the current type.
 643        /// </summary>
 644        /// <returns>
 645        /// A <see cref="TypeName" /> object that represents a pointer to the current type.
 646        /// </returns>
 0647        public TypeName MakePointerTypeName() => MakeElementTypeName(TypeNameParserHelpers.Pointer);
 648
 649        /// <summary>
 650        /// Creates a <see cref="TypeName" /> object that represents a managed reference to the current type.
 651        /// </summary>
 652        /// <returns>
 653        /// A <see cref="TypeName" /> object that represents a managed reference to the current type.
 654        /// </returns>
 0655        public TypeName MakeByRefTypeName() => MakeElementTypeName(TypeNameParserHelpers.ByRef);
 656
 657        /// <summary>
 658        /// Creates a new constructed generic type name.
 659        /// </summary>
 660        /// <param name="typeArguments">An array of type names to be used as generic arguments of the current simple typ
 661        /// <returns>
 662        /// A <see cref="TypeName" /> representing the constructed type name formed by using the elements
 663        /// of <paramref name="typeArguments"/> for the generic arguments of the current simple type name.
 664        /// </returns>
 665        /// <exception cref="InvalidOperationException">The current type name is not simple.</exception>
 666        public TypeName MakeGenericTypeName(ImmutableArray<TypeName> typeArguments)
 0667        {
 0668            if (!IsSimple)
 0669            {
 0670                TypeNameParserHelpers.ThrowInvalidOperation_NotSimpleName(FullName);
 671            }
 672
 0673            return new TypeName(fullName: null, AssemblyName, elementOrGenericType: this, declaringType: _declaringType,
 0674        }
 675
 676        private TypeName MakeElementTypeName(int rankOrModifier)
 0677            => new TypeName(
 0678                fullName: null,
 0679                assemblyName: AssemblyName,
 0680                elementOrGenericType: this,
 0681                declaringType: null,
 0682                genericTypeArguments: ImmutableArray<TypeName>.Empty,
 0683                rankOrModifier: rankOrModifier);
 684#endif
 685    }
 686}
 687