File: System\Reflection\Metadata\AssemblyNameInfo.cs
Web Access
Project: src\src\libraries\System.Reflection.Metadata\src\System.Reflection.Metadata.csproj (System.Reflection.Metadata)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Text;
 
#if !SYSTEM_PRIVATE_CORELIB
using System.Collections.Immutable;
using System.Linq;
#endif
 
namespace System.Reflection.Metadata
{
    /// <summary>
    /// Describes an assembly.
    /// </summary>
    /// <remarks>
    /// It's a more lightweight, immutable version of <seealso cref="AssemblyName"/> that does not pre-allocate <seealso cref="System.Globalization.CultureInfo"/> instances.
    /// </remarks>
    [DebuggerDisplay("{FullName}")]
#if SYSTEM_REFLECTION_METADATA
    public
#else
    internal
#endif
    sealed class AssemblyNameInfo
    {
        internal readonly AssemblyNameFlags _flags;
        private string? _fullName;
 
#if !SYSTEM_PRIVATE_CORELIB
        /// <summary>
        /// Initializes a new instance of the AssemblyNameInfo class.
        /// </summary>
        /// <param name="name">The simple name of the assembly.</param>
        /// <param name="version">The version of the assembly.</param>
        /// <param name="cultureName">The name of the culture associated with the assembly.</param>
        /// <param name="flags">The attributes of the assembly.</param>
        /// <param name="publicKeyOrToken">The public key or its token. Set <paramref name="flags"/> to <seealso cref="AssemblyNameFlags.PublicKey"/> when it's public key.</param>
        /// <exception cref="ArgumentNullException"><paramref name="name"/> is null.</exception>
        public AssemblyNameInfo(string name, Version? version = null, string? cultureName = null, AssemblyNameFlags flags = AssemblyNameFlags.None, ImmutableArray<byte> publicKeyOrToken = default)
        {
            Name = name ?? throw new ArgumentNullException(nameof(name));
            Version = version;
            CultureName = cultureName;
            _flags = flags;
            PublicKeyOrToken = publicKeyOrToken;
        }
#endif
 
        internal AssemblyNameInfo(AssemblyNameParser.AssemblyNameParts parts)
        {
            Name = parts._name;
            Version = parts._version;
            CultureName = parts._cultureName;
            _flags = parts._flags;
#if SYSTEM_PRIVATE_CORELIB
            PublicKeyOrToken = parts._publicKeyOrToken;
#else
            PublicKeyOrToken = parts._publicKeyOrToken is null ? default : parts._publicKeyOrToken.Length == 0
                ? ImmutableArray<byte>.Empty
    #if NET8_0_OR_GREATER
                : Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(parts._publicKeyOrToken);
    #else
                : ImmutableArray.Create(parts._publicKeyOrToken);
    #endif
#endif
        }
 
        /// <summary>
        /// Gets the simple name of the assembly.
        /// </summary>
        public string Name { get; }
 
        /// <summary>
        /// Gets the version of the assembly.
        /// </summary>
        public Version? Version { get; }
 
        /// <summary>
        /// Gets the name of the culture associated with the assembly.
        /// </summary>
        /// <remarks>
        /// Do not create a <see cref="System.Globalization.CultureInfo"/> instance from this string unless
        /// you know the string has originated from a trustworthy source.
        /// </remarks>
        public string? CultureName { get; }
 
        /// <summary>
        /// Gets the attributes of the assembly.
        /// </summary>
        public AssemblyNameFlags Flags => _flags;
 
        /// <summary>
        /// Gets the public key or the public key token of the assembly.
        /// </summary>
        /// <remarks>Check <seealso cref="Flags"/> for <seealso cref="AssemblyNameFlags.PublicKey"/> flag to see whether it's public key or its token.</remarks>
#if SYSTEM_PRIVATE_CORELIB
        public byte[]? PublicKeyOrToken { get; }
#else
        public ImmutableArray<byte> PublicKeyOrToken { get; }
#endif
 
        /// <summary>
        /// Gets the full name of the assembly, also known as the display name.
        /// </summary>
        /// <remarks>In contrary to <seealso cref="AssemblyName.FullName"/> it does not validate public key token neither computes it based on the provided public key.</remarks>
        public string FullName
        {
            get
            {
                if (_fullName is null)
                {
                    bool isPublicKey = (Flags & AssemblyNameFlags.PublicKey) != 0;
 
                    byte[]? publicKeyOrToken =
#if SYSTEM_PRIVATE_CORELIB
                    PublicKeyOrToken;
#elif NET8_0_OR_GREATER
                    !PublicKeyOrToken.IsDefault ? Runtime.InteropServices.ImmutableCollectionsMarshal.AsArray(PublicKeyOrToken) : null;
#else
                    !PublicKeyOrToken.IsDefault ? PublicKeyOrToken.ToArray() : null;
#endif
                    _fullName = AssemblyNameFormatter.ComputeDisplayName(Name, Version, CultureName,
                        pkt: isPublicKey ? null : publicKeyOrToken,
                        ExtractAssemblyNameFlags(_flags), ExtractAssemblyContentType(_flags),
                        pk: isPublicKey ? publicKeyOrToken : null);
                }
 
                return _fullName;
            }
        }
 
        /// <summary>
        /// Initializes a new instance of the <seealso cref="AssemblyName"/> class based on the stored information.
        /// </summary>
        /// <remarks>
        /// Do not create an <see cref="AssemblyName"/> instance with <see cref="CultureName"/> string unless
        /// you know the string has originated from a trustworthy source.
        /// </remarks>
        public AssemblyName ToAssemblyName()
        {
            AssemblyName assemblyName = new();
            assemblyName.Name = Name;
            assemblyName.CultureName = CultureName;
            assemblyName.Version = Version;
            assemblyName.Flags = Flags;
            assemblyName.ContentType = ExtractAssemblyContentType(_flags);
#pragma warning disable SYSLIB0037 // Type or member is obsolete
            assemblyName.ProcessorArchitecture = ExtractProcessorArchitecture(_flags);
#pragma warning restore SYSLIB0037 // Type or member is obsolete
 
#if SYSTEM_PRIVATE_CORELIB
            if (PublicKeyOrToken is not null)
            {
                if ((Flags & AssemblyNameFlags.PublicKey) != 0)
                {
                    assemblyName.SetPublicKey(PublicKeyOrToken);
                }
                else
                {
                    assemblyName.SetPublicKeyToken(PublicKeyOrToken);
                }
            }
#else
            if (!PublicKeyOrToken.IsDefault)
            {
                // A copy of the array needs to be created, as AssemblyName allows for the mutation of provided array.
                if ((Flags & AssemblyNameFlags.PublicKey) != 0)
                {
                    assemblyName.SetPublicKey(PublicKeyOrToken.ToArray());
                }
                else
                {
                    assemblyName.SetPublicKeyToken(PublicKeyOrToken.ToArray());
                }
            }
#endif
 
            return assemblyName;
        }
 
        /// <summary>
        /// Parses a span of characters into a assembly name.
        /// </summary>
        /// <param name="assemblyName">A span containing the characters representing the assembly name to parse.</param>
        /// <returns>Parsed type name.</returns>
        /// <exception cref="ArgumentException">Provided assembly name was invalid.</exception>
        public static AssemblyNameInfo Parse(ReadOnlySpan<char> assemblyName)
            => TryParse(assemblyName, out AssemblyNameInfo? result)
                ? result!
                : throw new ArgumentException(SR.InvalidAssemblyName, nameof(assemblyName));
 
        /// <summary>
        /// Tries to parse a span of characters into an assembly name.
        /// </summary>
        /// <param name="assemblyName">A span containing the characters representing the assembly name to parse.</param>
        /// <param name="result">Contains the result when parsing succeeds.</param>
        /// <returns>true if assembly name was converted successfully, otherwise, false.</returns>
        public static bool TryParse(ReadOnlySpan<char> assemblyName, [NotNullWhen(true)] out AssemblyNameInfo? result)
        {
            AssemblyNameParser.AssemblyNameParts parts = default;
            if (!assemblyName.IsEmpty && AssemblyNameParser.TryParse(assemblyName, ref parts))
            {
                result = new(parts);
                return true;
            }
 
            result = null;
            return false;
        }
 
        internal static AssemblyNameFlags ExtractAssemblyNameFlags(AssemblyNameFlags combinedFlags)
            => combinedFlags & unchecked((AssemblyNameFlags)0xFFFFF10F);
 
        internal static AssemblyContentType ExtractAssemblyContentType(AssemblyNameFlags flags)
            => (AssemblyContentType)((((int)flags) >> 9) & 0x7);
 
        internal static ProcessorArchitecture ExtractProcessorArchitecture(AssemblyNameFlags flags)
            => (ProcessorArchitecture)((((int)flags) >> 4) & 0x7);
    }
}