File: System\Text\Json\Serialization\Metadata\ReflectionEmitCachingMemberAccessor.cs
Web Access
Project: src\runtime\src\libraries\System.Text.Json\src\System.Text.Json.csproj (System.Text.Json)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

#if NETFRAMEWORK || NET
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;

namespace System.Text.Json.Serialization.Metadata
{
    internal sealed partial class ReflectionEmitCachingMemberAccessor : MemberAccessor
    {
        private readonly ReflectionEmitMemberAccessor _sourceAccessor;
        private readonly Cache<(string id, Type declaringType, MemberInfo? member)> _cache;

        [RequiresDynamicCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
        [RequiresUnreferencedCode(JsonSerializer.SerializationRequiresDynamicCodeMessage)]
        public ReflectionEmitCachingMemberAccessor()
        {
            _sourceAccessor = new ReflectionEmitMemberAccessor();
            _cache = new(slidingExpiration: TimeSpan.FromMilliseconds(1000), evictionInterval: TimeSpan.FromMilliseconds(200));
        }

        public override void Clear() => _cache.Clear();

        public override Action<TCollection, object?> CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TCollection>() =>
            _cache.GetOrAdd(
                key: (nameof(CreateAddMethodDelegate), typeof(TCollection), null),
                _ => _sourceAccessor.CreateAddMethodDelegate<TCollection>());

        public override Func<object>? CreateParameterlessConstructor(Type type, ConstructorInfo? ctorInfo) =>
            _cache.GetOrAdd(
                key: (nameof(CreateParameterlessConstructor), type, ctorInfo),
                valueFactory: key => _sourceAccessor.CreateParameterlessConstructor(key.declaringType, (ConstructorInfo?)key.member));

        public override Func<object, TProperty> CreateFieldGetter<TProperty>(FieldInfo fieldInfo) =>
            _cache.GetOrAdd(
                key: (nameof(CreateFieldGetter), typeof(TProperty), fieldInfo),
                valueFactory: key => _sourceAccessor.CreateFieldGetter<TProperty>((FieldInfo)key.member!));

        public override Action<object, TProperty> CreateFieldSetter<TProperty>(FieldInfo fieldInfo) =>
            _cache.GetOrAdd(
                key: (nameof(CreateFieldSetter), typeof(TProperty), fieldInfo),
                valueFactory: key => _sourceAccessor.CreateFieldSetter<TProperty>((FieldInfo)key.member!));

        public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>() =>
            _cache.GetOrAdd(
                key: (nameof(CreateImmutableDictionaryCreateRangeDelegate), typeof((TCollection, TKey, TValue)), null),
                valueFactory: _ => _sourceAccessor.CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>());

        public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>() =>
            _cache.GetOrAdd(
                key: (nameof(CreateImmutableEnumerableCreateRangeDelegate), typeof((TCollection, TElement)), null),
                valueFactory: _ => _sourceAccessor.CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>());

        public override Func<object[], T> CreateParameterizedConstructor<T>(ConstructorInfo constructor) =>
            _cache.GetOrAdd(
                key: (nameof(CreateParameterizedConstructor), typeof(T), constructor),
                valueFactory: key => _sourceAccessor.CreateParameterizedConstructor<T>((ConstructorInfo)key.member!));

        public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, TArg2, TArg3>? CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>(ConstructorInfo constructor) =>
            _cache.GetOrAdd(
                key: (nameof(CreateParameterizedConstructor), typeof(T), constructor),
                valueFactory: key => _sourceAccessor.CreateParameterizedConstructor<T, TArg0, TArg1, TArg2, TArg3>((ConstructorInfo)key.member!));

        public override Func<object?, T> CreateSingleParameterConstructor<T>(ConstructorInfo constructor) =>
            _cache.GetOrAdd(
                key: (nameof(CreateSingleParameterConstructor), typeof(T), constructor),
                valueFactory: key => _sourceAccessor.CreateSingleParameterConstructor<T>((ConstructorInfo)key.member!));

        public override Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo) =>
            _cache.GetOrAdd(
                key: (nameof(CreatePropertyGetter), typeof(TProperty), propertyInfo),
                valueFactory: key => _sourceAccessor.CreatePropertyGetter<TProperty>((PropertyInfo)key.member!));

        public override Func<TDeclaringType, TProperty> CreatePropertyGetter<TDeclaringType, TProperty>(PropertyInfo propertyInfo) =>
            _cache.GetOrAdd(
                key: ("CreatePropertyGetter`2", typeof((TDeclaringType, TProperty)), propertyInfo),
                valueFactory: key => _sourceAccessor.CreatePropertyGetter<TDeclaringType, TProperty>((PropertyInfo)key.member!));

        public override Action<object, TProperty> CreatePropertySetter<TProperty>(PropertyInfo propertyInfo) =>
            _cache.GetOrAdd(
                key: (nameof(CreatePropertySetter), typeof(TProperty), propertyInfo),
                valueFactory: key => _sourceAccessor.CreatePropertySetter<TProperty>((PropertyInfo)key.member!));

        public override UnionTryGetValueAccessor<TUnion> CreateUnionTryGetValueAccessor<TUnion>(IReadOnlyList<KeyValuePair<Type, MethodInfo>> entries) =>
            // The chained accessor is built once per JsonTypeInfo<TUnion> during metadata
            // population, so an additional cache layer would not pay for itself; forward
            // directly to the underlying emit-backed accessor.
            _sourceAccessor.CreateUnionTryGetValueAccessor<TUnion>(entries);
    }
}
#endif