File: src\RoslynAnalyzers\Utilities\FlowAnalysis\FlowAnalysis\Framework\DataFlow\CacheBasedEquatable.cs
Web Access
Project: src\src\RoslynAnalyzers\Microsoft.CodeAnalysis.AnalyzerUtilities\Microsoft.CodeAnalysis.AnalyzerUtilities.csproj (Microsoft.CodeAnalysis.AnalyzerUtilities)
// 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.
 
using System;
using System.Threading;
using Analyzer.Utilities;
 
#pragma warning disable CA2002
 
namespace Microsoft.CodeAnalysis.FlowAnalysis.DataFlow
{
    /// <summary>
    /// Abstract cache based equatable implementation for objects that are compared frequently and hence need a performance optimization of using a cached hash code.
    /// </summary>
    public abstract class CacheBasedEquatable<T> : IEquatable<T?>
        where T : class
    {
        private int _lazyHashCode;
 
        protected CacheBasedEquatable()
        {
        }
 
        private int GetOrComputeHashCode()
        {
            if (_lazyHashCode == 0)
            {
                var hashCode = new RoslynHashCode();
                ComputeHashCodeParts(ref hashCode);
                var result = hashCode.ToHashCode();
                Interlocked.CompareExchange(ref _lazyHashCode, result, 0);
            }
 
            return _lazyHashCode;
        }
 
        protected abstract void ComputeHashCodeParts(ref RoslynHashCode hashCode);
 
        protected abstract bool ComputeEqualsByHashCodeParts(CacheBasedEquatable<T> obj);
 
        public sealed override int GetHashCode() => GetOrComputeHashCode();
 
        public sealed override bool Equals(object? obj) => Equals(obj as T);
        public bool Equals(T? other)
        {
            // Perform fast equality checks first.
            if (ReferenceEquals(this, other))
            {
                return true;
            }
 
            var otherEquatable = other as CacheBasedEquatable<T>;
            if (otherEquatable == null
                || GetType() != otherEquatable.GetType()
                || GetHashCode() != otherEquatable.GetHashCode())
            {
                return false;
            }
 
            // Now perform slow check that compares individual hash code parts sequences.
            return ComputeEqualsByHashCodeParts(otherEquatable);
        }
 
        public static bool operator ==(CacheBasedEquatable<T>? value1, CacheBasedEquatable<T>? value2)
        {
            if (value1 is null)
            {
                return value2 is null;
            }
            else if (value2 is null)
            {
                return false;
            }
 
            return value1.Equals(value2);
        }
 
        public static bool operator !=(CacheBasedEquatable<T>? value1, CacheBasedEquatable<T>? value2)
        {
            return !(value1 == value2);
        }
    }
}