File: TypeRef.cs
Web Access
Project: src\src\LanguageServer\Microsoft.CommonLanguageServerProtocol.Framework\Microsoft.CommonLanguageServerProtocol.Framework.Package.csproj (Microsoft.CommonLanguageServerProtocol.Framework.Package)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
// This is consumed as 'generated' code in a source package and therefore requires an explicit nullable enable
#nullable enable
 
using System;
using System.Collections.Concurrent;
 
namespace Microsoft.CommonLanguageServerProtocol.Framework;
 
/// <summary>
/// Helper that avoids loading a <see cref="Type"/> by its full assembly-qualified name until needed.
/// </summary>
internal sealed partial class TypeRef : IEquatable<TypeRef>
{
    private static readonly ConcurrentDictionary<(string TypeName, string AssemblyName, string? CodeBase), TypeRef> s_cache = [];
 
    private string? _assemblyQualifiedName;
 
    private TypeRef(string typeName, string assemblyName, string? codeBase = null)
    {
        TypeName = typeName ?? throw new ArgumentNullException(nameof(typeName));
        AssemblyName = assemblyName ?? throw new ArgumentNullException(nameof(assemblyName));
        CodeBase = codeBase;
    }
 
    /// <summary>
    /// Returns the full name of this type.
    /// </summary>
    public string TypeName { get; }
 
    /// <summary>
    /// Returns the full name of the assembly containing this type.
    /// </summary>
    public string AssemblyName { get; }
 
    /// <summary>
    /// Returns the code base of the assembly containing this type, if any.
    /// </summary>
    public string? CodeBase { get; }
 
    /// <summary>
    /// Returns the assembly-qualified name of this type.
    /// </summary>
    public string AssemblyQualifiedName
        => _assemblyQualifiedName ??= $"{TypeName}, {AssemblyName}";
 
    public override bool Equals(object? obj)
        => Equals(obj as TypeRef);
 
    public bool Equals(TypeRef? other)
        => other is not null &&
           TypeName == other.TypeName &&
           AssemblyName == other.AssemblyName &&
           CodeBase == other.CodeBase;
 
    public override int GetHashCode()
    {
        var comparer = StringComparer.Ordinal;
 
        var hashCode = 2037759866;
        unchecked
        {
            hashCode = hashCode * -1521134295 + comparer.GetHashCode(TypeName);
            hashCode = hashCode * -1521134295 + comparer.GetHashCode(AssemblyName);
 
            if (CodeBase is string codeBase)
            {
                hashCode = hashCode * -1521134295 + comparer.GetHashCode(codeBase);
            }
        }
        return hashCode;
    }
 
    public override string ToString() => TypeName;
 
    /// <summary>
    /// Constructs a <see cref="TypeRef"/> instance.
    /// </summary>
    /// <param name="typeName">The full name of this type.</param>
    /// <param name="assemblyName">The full name of the assembly containing this type.</param>
    /// <param name="codeBase">The code base of the assembly containing this type, if any.</param>
    public static TypeRef From(string typeName, string assemblyName, string? codeBase)
    {
        var key = (typeName, assemblyName, codeBase);
 
        if (s_cache.TryGetValue(key, out var result))
        {
            return result;
        }
 
        return s_cache.GetOrAdd(key, new TypeRef(typeName, assemblyName, codeBase));
    }
 
    /// <summary>
    /// Constructs a <see cref="TypeRef"/> from a <see cref="Type"/>.
    /// </summary>
    /// <param name="type">The <see cref="Type"/> to use.</param>
    public static TypeRef From(Type type)
    {
        if (type is null)
        {
            throw new ArgumentNullException(nameof(type));
        }
 
        var typeName = type.FullName ?? throw new ArgumentException($"{nameof(type)} has null {nameof(type.FullName)} property.", nameof(type));
        var assemblyName = type.Assembly.FullName ?? throw new ArgumentException($"{nameof(type)} has null {nameof(type.Assembly)}.{nameof(type.Assembly.FullName)} property.", nameof(type));
 
#pragma warning disable SYSLIB0012 // Type or member is obsolete
        var codeBase = type.Assembly.CodeBase;
#pragma warning restore SYSLIB0012 // Type or member is obsolete
 
        return From(typeName, assemblyName, codeBase);
    }
 
    /// <summary>
    /// Constructs a <see cref="TypeRef"/> from a <see cref="Type"/> or returns <see langword="null"/>
    /// </summary>
    /// <param name="type">The <see cref="Type"/> to use, or <see langword="null"/>.</param>
    public static TypeRef? FromOrNull(Type? type)
        => type is not null ? From(type) : null;
 
    public static TypeRef Of<T>() => From(typeof(T));
}