File: DiagnosticAnalyzer\CompilationAnalysisValueProvider.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// 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.
 
#nullable disable
 
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
 
namespace Microsoft.CodeAnalysis.Diagnostics
{
    /// <summary>
    /// Wrapper over the core <see cref="AnalysisValueProvider{TKey, TValue}"/> which holds a strong reference to key-value pairs for the lifetime of a compilation that this provider is associated with.
    /// This ensures that values are never re-computed for equivalent keys while analyzing each compilation, improving overall analyzer performance.
    /// </summary>
    internal sealed class CompilationAnalysisValueProvider<TKey, TValue>
        where TKey : class
    {
        private readonly AnalysisValueProvider<TKey, TValue> _analysisValueProvider;
        private readonly Dictionary<TKey, TValue> _valueMap;
 
        public CompilationAnalysisValueProvider(AnalysisValueProvider<TKey, TValue> analysisValueProvider)
        {
            _analysisValueProvider = analysisValueProvider;
            _valueMap = new Dictionary<TKey, TValue>(analysisValueProvider.KeyComparer);
        }
 
        internal bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
        {
            // First try to get the cached value for this compilation.
            lock (_valueMap)
            {
                if (_valueMap.TryGetValue(key, out value))
                {
                    return true;
                }
            }
 
            // Ask the core analysis value provider for the value.
            // We do it outside the lock statement as this may call into user code which can be a long running operation.
            if (!_analysisValueProvider.TryGetValue(key, out value))
            {
                value = default(TValue);
                return false;
            }
 
            // Store the value for the lifetime of the compilation.
            lock (_valueMap)
            {
                // Check if another thread already stored the computed value.
                TValue storedValue;
                if (_valueMap.TryGetValue(key, out storedValue))
                {
                    // If so, we return the stored value.
                    value = storedValue;
                }
                else
                {
                    // Otherwise, store the value computed here.
                    _valueMap.Add(key, value);
                }
            }
 
            return true;
        }
    }
}