File: DiagnosticAnalyzer\AnalysisValueProvider.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.
 
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
 
namespace Microsoft.CodeAnalysis.Diagnostics
{
    internal class AnalysisValueProvider<TKey, TValue>
        where TKey : class
    {
        private readonly Func<TKey, TValue> _computeValue;
 
        // This provider holds a weak reference to the key-value pairs, as AnalysisValueProvider might outlive individual compilations.
        // CompilationAnalysisValueProvider, which wraps this provider and lives for the lifetime of specific compilation, holds a strong reference to the key-value pairs, providing an overall performance benefit.
        private readonly ConditionalWeakTable<TKey, WrappedValue> _valueCache;
        private readonly ConditionalWeakTable<TKey, WrappedValue>.CreateValueCallback _valueCacheCallback;
 
        internal IEqualityComparer<TKey> KeyComparer { get; private set; }
 
        public AnalysisValueProvider(Func<TKey, TValue> computeValue, IEqualityComparer<TKey> keyComparer)
        {
            _computeValue = computeValue;
            KeyComparer = keyComparer ?? EqualityComparer<TKey>.Default;
            _valueCache = new ConditionalWeakTable<TKey, WrappedValue>();
            _valueCacheCallback = new ConditionalWeakTable<TKey, WrappedValue>.CreateValueCallback(ComputeValue);
        }
 
        private sealed class WrappedValue
        {
            public WrappedValue(TValue value)
            {
                Value = value;
            }
 
            public TValue Value { get; }
        }
 
        private WrappedValue ComputeValue(TKey key)
        {
            var value = _computeValue(key);
            return new WrappedValue(value);
        }
 
        internal bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
        {
            // Catch any exceptions from the computeValue callback, which calls into user code.
            try
            {
                value = _valueCache.GetValue(key, _valueCacheCallback).Value;
                Debug.Assert(value is object);
                return true;
            }
            catch (Exception)
            {
                value = default;
                return false;
            }
        }
    }
}