File: System\Xml\Serialization\ContextAwareTables.cs
Web Access
Project: src\src\libraries\System.Private.Xml\src\System.Private.Xml.csproj (System.Private.Xml)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.Loader;
 
namespace System.Xml.Serialization
{
    internal sealed class ContextAwareTables<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] T> where T : class?
    {
        private readonly Hashtable _defaultTable;
        private readonly ConditionalWeakTable<Type, T> _collectibleTable;
 
        public ContextAwareTables()
        {
            _defaultTable = new Hashtable();
            _collectibleTable = new ConditionalWeakTable<Type, T>();
        }
 
        internal T GetOrCreateValue(Type t, Func<Type, T> f)
        {
            // The fast and most common default case
            T? ret = (T?)_defaultTable[t];
            if (ret != null)
                return ret;
 
            // Common case for collectible contexts
            if (_collectibleTable.TryGetValue(t, out ret))
                return ret;
 
            // Not found. Do the slower work of creating the value in the correct collection.
            AssemblyLoadContext? alc = AssemblyLoadContext.GetLoadContext(t.Assembly);
 
            // Null and non-collectible load contexts use the default table
            if (alc == null || !alc.IsCollectible)
            {
                lock (_defaultTable)
                {
                    if ((ret = (T?)_defaultTable[t]) == null)
                    {
                        ret = f(t);
                        _defaultTable[t] = ret;
                    }
                }
            }
 
            // Collectible load contexts should use the ConditionalWeakTable so they can be unloaded
            else
            {
                lock (_collectibleTable)
                {
                    if (!_collectibleTable.TryGetValue(t, out ret))
                    {
                        ret = f(t);
                        _collectibleTable.AddOrUpdate(t, ret);
                    }
                }
            }
 
            return ret;
        }
    }
}