File: System\Reflection\TypeLoading\RuntimeTypeInfo.TypeComponentsCache.cs
Web Access
Project: src\src\libraries\System.Reflection.MetadataLoadContext\src\System.Reflection.MetadataLoadContext.csproj (System.Reflection.MetadataLoadContext)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections.Concurrent;
using System.Reflection.Runtime.BindingFlagSupport;
using System.Threading;
using RuntimeTypeInfo = System.Reflection.TypeLoading.RoType;
 
namespace System.Reflection.TypeLoading
{
    internal partial class RoType
    {
        /// <summary>
        /// TypeComponentsCache objects are allocated on-demand on a per-Type basis to cache hot data for key scenarios.
        /// To maximize throughput once the cache is created, the object creates all of its internal caches up front
        /// and holds entries strongly (and relying on the fact that Types themselves are held weakly to avoid immortality.)
        ///
        /// Note that it is possible that two threads racing to query the same TypeInfo may allocate and query two different
        /// cache objects. Thus, this object must not be relied upon to preserve object identity.
        /// </summary>
 
        private sealed class TypeComponentsCache
        {
            public TypeComponentsCache(RuntimeTypeInfo type)
            {
                _type = type;
 
                _perNameQueryCaches_CaseSensitive = CreatePerNameQueryCaches(type, ignoreCase: false, immediateTypeOnly: false);
                _perNameQueryCaches_CaseInsensitive = CreatePerNameQueryCaches(type, ignoreCase: true, immediateTypeOnly: false);
 
                _perNameQueryCaches_CaseSensitive_ImmediateTypeOnly = CreatePerNameQueryCaches(type, ignoreCase: false, immediateTypeOnly: true);
                _perNameQueryCaches_CaseInsensitive_ImmediateTypeOnly = CreatePerNameQueryCaches(type, ignoreCase: true, immediateTypeOnly: true);
 
                _nameAgnosticQueryCaches = new object[MemberTypeIndex.Count];
            }
 
            //
            // Returns the cached result of a name-specific query on the Type's members, as if you'd passed in
            //
            //  BindingFlags == Public | NonPublic | Instance | Static | FlattenHierarchy  (immediateTypeOnly == false)
            //                  Public | NonPublic | Instance | Static | DeclaredOnly      (immediateTypeOnly == true)
            //
            public QueriedMemberList<M> GetQueriedMembers<M>(string name, bool ignoreCase, bool immediateTypeOnly) where M : MemberInfo
            {
                int index = MemberPolicies<M>.MemberTypeIndex;
                object[] cacheArray = ignoreCase ?
                    (immediateTypeOnly ? _perNameQueryCaches_CaseInsensitive_ImmediateTypeOnly : _perNameQueryCaches_CaseInsensitive) :
                    (immediateTypeOnly ? _perNameQueryCaches_CaseSensitive_ImmediateTypeOnly : _perNameQueryCaches_CaseSensitive);
 
                object unifierAsObject = cacheArray[index];
                PerNameQueryCache<M> unifier = (PerNameQueryCache<M>)unifierAsObject;
                QueriedMemberList<M> result = unifier.GetOrAdd(name);
                return result;
            }
 
            //
            // Returns the cached result of a name-agnostic query on the Type's members, as if you'd passed in
            //
            //  BindingFlags == Public | NonPublic | Instance | Static | FlattenHierarchy  (immediateTypeOnly == false)
            //                  Public | NonPublic | Instance | Static | DeclaredOnly      (immediateTypeOnly == true)
            //
            public QueriedMemberList<M> GetQueriedMembers<M>(bool immediateTypeOnly) where M : MemberInfo
            {
                int index = MemberPolicies<M>.MemberTypeIndex;
                object result = Volatile.Read(ref _nameAgnosticQueryCaches[index]);
                if (result == null)
                {
                    QueriedMemberList<M> newResult = QueriedMemberList<M>.Create(_type, filter: null, ignoreCase: false, immediateTypeOnly: immediateTypeOnly);
                    newResult.Compact();
                    Volatile.Write(ref _nameAgnosticQueryCaches[index], newResult);
                    return newResult;
                }
                QueriedMemberList<M> list = (QueriedMemberList<M>)result;
                if (list.ImmediateTypeOnly && !immediateTypeOnly)
                {
                    QueriedMemberList<M> newResult = QueriedMemberList<M>.Create(_type, filter: null, ignoreCase: false, immediateTypeOnly: false);
                    newResult.Compact();
                    Volatile.Write(ref _nameAgnosticQueryCaches[index], newResult);
                    return newResult;
                }
                return list;
            }
 
            private static object[] CreatePerNameQueryCaches(RuntimeTypeInfo type, bool ignoreCase, bool immediateTypeOnly)
            {
                object[] perNameCaches = new object[MemberTypeIndex.Count];
                perNameCaches[MemberTypeIndex.Constructor] = new PerNameQueryCache<ConstructorInfo>(type, ignoreCase: ignoreCase, immediateTypeOnly: immediateTypeOnly);
                perNameCaches[MemberTypeIndex.Event] = new PerNameQueryCache<EventInfo>(type, ignoreCase: ignoreCase, immediateTypeOnly: immediateTypeOnly);
                perNameCaches[MemberTypeIndex.Field] = new PerNameQueryCache<FieldInfo>(type, ignoreCase: ignoreCase, immediateTypeOnly: immediateTypeOnly);
                perNameCaches[MemberTypeIndex.Method] = new PerNameQueryCache<MethodInfo>(type, ignoreCase: ignoreCase, immediateTypeOnly: immediateTypeOnly);
                perNameCaches[MemberTypeIndex.Property] = new PerNameQueryCache<PropertyInfo>(type, ignoreCase: ignoreCase, immediateTypeOnly: immediateTypeOnly);
                perNameCaches[MemberTypeIndex.NestedType] = new PerNameQueryCache<Type>(type, ignoreCase: ignoreCase, immediateTypeOnly: immediateTypeOnly);
                return perNameCaches;
            }
 
            // This array holds six PerNameQueryCache<M> objects, one for each of the possible M types (ConstructorInfo, EventInfo, etc.)
            // The caches are configured to do a case-sensitive query.
            private readonly object[] _perNameQueryCaches_CaseSensitive;
 
            // This array holds six PerNameQueryCache<M> objects, one for each of the possible M types (ConstructorInfo, EventInfo, etc.)
            // The caches are configured to do a case-insensitive query.
            private readonly object[] _perNameQueryCaches_CaseInsensitive;
 
            // This array holds six PerNameQueryCache<M> objects, one for each of the possible M types (ConstructorInfo, EventInfo, etc.)
            // The caches are configured to do a case-sensitive query but do not contain results from base types.
            private readonly object[] _perNameQueryCaches_CaseSensitive_ImmediateTypeOnly;
 
            // This array holds six PerNameQueryCache<M> objects, one for each of the possible M types (ConstructorInfo, EventInfo, etc.)
            // The caches are configured to do a case-insensitive query but do not contain results from base types.
            private readonly object[] _perNameQueryCaches_CaseInsensitive_ImmediateTypeOnly;
 
            // This array holds six lazily created QueriedMemberList<M> objects, one for each of the possible M types (ConstructorInfo, EventInfo, etc.).
            // The objects are the results of a name-agnostic query.
            private readonly object[] _nameAgnosticQueryCaches;
 
            private readonly RuntimeTypeInfo _type;
 
            //
            // Each PerName cache persists the results of a Type.Get(name, bindingFlags) for a particular MemberInfoType "M".
            //
            // where "bindingFlags" == Public | NonPublic | Instance | Static | FlattenHierarchy for the total caches and
            //                         Public | NonPublic | Instance | Static | DeclaredOnly for the ImmediateTypeOnly caches.
            //
            // In addition, if "ignoreCase" was passed to the constructor, BindingFlags.IgnoreCase is also in effect.
            //
            private sealed class PerNameQueryCache<M> : ConcurrentUnifier<string, QueriedMemberList<M>> where M : MemberInfo
            {
                public PerNameQueryCache(RuntimeTypeInfo type, bool ignoreCase, bool immediateTypeOnly)
                {
                    _type = type;
                    _ignoreCase = ignoreCase;
                    _immediateTypeOnly = immediateTypeOnly;
                }
 
                protected sealed override QueriedMemberList<M> Factory(string key)
                {
                    QueriedMemberList<M> result = QueriedMemberList<M>.Create(_type, key, ignoreCase: _ignoreCase, immediateTypeOnly: _immediateTypeOnly);
                    result.Compact();
                    return result;
                }
 
                private readonly RuntimeTypeInfo _type;
                private readonly bool _ignoreCase;
                private readonly bool _immediateTypeOnly;
            }
        }
    }
}