22// The .NET Foundation licenses this file to you under the MIT license.
33
44using System ;
5+ using System . Collections . Concurrent ;
56using System . Diagnostics ;
67using System . Diagnostics . CodeAnalysis ;
78using System . Linq . Expressions ;
1011using System . Runtime . ExceptionServices ;
1112using Microsoft . Extensions . Internal ;
1213
14+ #if NETCOREAPP
15+ [ assembly: System . Reflection . Metadata . MetadataUpdateHandler ( typeof ( Microsoft . Extensions . DependencyInjection . ActivatorUtilities . ActivatorUtilitiesUpdateHandler ) ) ]
16+ #endif
17+
1318namespace Microsoft . Extensions . DependencyInjection
1419{
1520 /// <summary>
1621 /// Helper code for the various activator services.
1722 /// </summary>
1823 public static class ActivatorUtilities
1924 {
25+ #if NETCOREAPP
26+ // Support caching of constructor metadata for the common case of types in non-collectible assemblies.
27+ private static readonly ConcurrentDictionary < Type , ConstructorInfoEx [ ] > s_constructorInfos = new ( ) ;
28+
29+ // Support caching of constructor metadata for types in collectible assemblies.
30+ private static readonly Lazy < ConditionalWeakTable < Type , ConstructorInfoEx [ ] > > s_collectibleConstructorInfos = new ( ) ;
31+ #endif
32+
2033#if NET8_0_OR_GREATER
2134 // Maximum number of fixed arguments for ConstructorInvoker.Invoke(arg1, etc).
2235 private const int FixedArgumentThreshold = 4 ;
@@ -47,6 +60,17 @@ public static object CreateInstance(
4760 throw new InvalidOperationException ( SR . CannotCreateAbstractClasses ) ;
4861 }
4962
63+ ConstructorInfoEx [ ] ? constructors ;
64+ #if NETCOREAPP
65+ if ( ! s_constructorInfos . TryGetValue ( instanceType , out constructors ) )
66+ {
67+ constructors = GetOrAddConstructors ( instanceType ) ;
68+ }
69+ #else
70+ constructors = CreateConstructorInfoExs ( instanceType ) ;
71+ #endif
72+
73+ ConstructorInfoEx ? constructor ;
5074 IServiceProviderIsService ? serviceProviderIsService = provider . GetService < IServiceProviderIsService > ( ) ;
5175 // if container supports using IServiceProviderIsService, we try to find the longest ctor that
5276 // (a) matches all parameters given to CreateInstance
@@ -61,10 +85,11 @@ public static object CreateInstance(
6185 ConstructorMatcher bestMatcher = default ;
6286 bool multipleBestLengthFound = false ;
6387
64- foreach ( ConstructorInfo ? constructor in instanceType . GetConstructors ( ) )
88+ for ( int i = 0 ; i < constructors . Length ; i ++ )
6589 {
66- var matcher = new ConstructorMatcher ( constructor ) ;
67- bool isPreferred = constructor . IsDefined ( typeof ( ActivatorUtilitiesConstructorAttribute ) , false ) ;
90+ constructor = constructors [ i ] ;
91+ ConstructorMatcher matcher = new ( constructor ) ;
92+ bool isPreferred = constructor . IsPreferred ;
6893 int length = matcher . Match ( parameters , serviceProviderIsService ) ;
6994
7095 if ( isPreferred )
@@ -105,18 +130,79 @@ public static object CreateInstance(
105130 }
106131 }
107132
108- Type ? [ ] argumentTypes = new Type [ parameters . Length ] ;
109- for ( int i = 0 ; i < argumentTypes . Length ; i ++ )
133+ Type ? [ ] argumentTypes ;
134+ if ( parameters . Length == 0 )
110135 {
111- argumentTypes [ i ] = parameters [ i ] ? . GetType ( ) ;
136+ argumentTypes = Type . EmptyTypes ;
137+ }
138+ else
139+ {
140+ argumentTypes = new Type [ parameters . Length ] ;
141+ for ( int i = 0 ; i < argumentTypes . Length ; i ++ )
142+ {
143+ argumentTypes [ i ] = parameters [ i ] ? . GetType ( ) ;
144+ }
112145 }
113146
114147 FindApplicableConstructor ( instanceType , argumentTypes , out ConstructorInfo constructorInfo , out int ? [ ] parameterMap ) ;
115- var constructorMatcher = new ConstructorMatcher ( constructorInfo ) ;
148+
149+ // Find the ConstructorInfoEx from the given constructorInfo.
150+ constructor = null ;
151+ foreach ( ConstructorInfoEx ctor in constructors )
152+ {
153+ if ( ReferenceEquals ( ctor . Info , constructorInfo ) )
154+ {
155+ constructor = ctor ;
156+ break ;
157+ }
158+ }
159+
160+ Debug . Assert ( constructor != null ) ;
161+
162+ var constructorMatcher = new ConstructorMatcher ( constructor ) ;
116163 constructorMatcher . MapParameters ( parameterMap , parameters ) ;
117164 return constructorMatcher . CreateInstance ( provider ) ;
118165 }
119166
167+ #if NETCOREAPP
168+ private static ConstructorInfoEx [ ] GetOrAddConstructors (
169+ [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] Type type )
170+ {
171+ // Not found. Do the slower work of checking for the value in the correct cache.
172+ // Null and non-collectible load contexts use the default cache.
173+ if ( ! type . Assembly . IsCollectible )
174+ {
175+ return s_constructorInfos . GetOrAdd ( type , CreateConstructorInfoExs ( type ) ) ;
176+ }
177+
178+ // Collectible load contexts should use the ConditionalWeakTable so they can be unloaded.
179+ if ( s_collectibleConstructorInfos . Value . TryGetValue ( type , out ConstructorInfoEx [ ] ? value ) )
180+ {
181+ return value ;
182+ }
183+
184+ value = CreateConstructorInfoExs ( type ) ;
185+
186+ // ConditionalWeakTable doesn't support GetOrAdd() so use AddOrUpdate(). This means threads
187+ // can have different instances for the same type, but that is OK since they are equivalent.
188+ s_collectibleConstructorInfos . Value . AddOrUpdate ( type , value ) ;
189+ return value ;
190+ }
191+ #endif // NETCOREAPP
192+
193+ private static ConstructorInfoEx [ ] CreateConstructorInfoExs (
194+ [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors ) ] Type type )
195+ {
196+ ConstructorInfo [ ] constructors = type . GetConstructors ( ) ;
197+ ConstructorInfoEx [ ] ? value = new ConstructorInfoEx [ constructors . Length ] ;
198+ for ( int i = 0 ; i < constructors . Length ; i ++ )
199+ {
200+ value [ i ] = new ConstructorInfoEx ( constructors [ i ] ) ;
201+ }
202+
203+ return value ;
204+ }
205+
120206 /// <summary>
121207 /// Create a delegate that will instantiate a type with constructor arguments provided directly
122208 /// and/or from an <see cref="IServiceProvider"/>.
@@ -551,58 +637,82 @@ private static bool TryCreateParameterMap(ParameterInfo[] constructorParameters,
551637 return true ;
552638 }
553639
554- private static object ? GetService ( IServiceProvider serviceProvider , ParameterInfo parameterInfo )
640+ private sealed class ConstructorInfoEx
555641 {
556- // Handle keyed service
557- if ( TryGetServiceKey ( parameterInfo , out object ? key ) )
642+ public readonly ConstructorInfo Info ;
643+ public readonly ParameterInfo [ ] Parameters ;
644+ public readonly bool IsPreferred ;
645+ private readonly object ? [ ] ? _parameterKeys ;
646+
647+ public ConstructorInfoEx ( ConstructorInfo constructor )
558648 {
559- if ( serviceProvider is IKeyedServiceProvider keyedServiceProvider )
649+ Info = constructor ;
650+ Parameters = constructor . GetParameters ( ) ;
651+ IsPreferred = constructor . IsDefined ( typeof ( ActivatorUtilitiesConstructorAttribute ) , inherit : false ) ;
652+
653+ for ( int i = 0 ; i < Parameters . Length ; i ++ )
560654 {
561- return keyedServiceProvider . GetKeyedService ( parameterInfo . ParameterType , key ) ;
655+ FromKeyedServicesAttribute ? attr = ( FromKeyedServicesAttribute ? )
656+ Attribute . GetCustomAttribute ( Parameters [ i ] , typeof ( FromKeyedServicesAttribute ) , inherit : false ) ;
657+
658+ if ( attr is not null )
659+ {
660+ _parameterKeys ??= new object ? [ Parameters . Length ] ;
661+ _parameterKeys [ i ] = attr . Key ;
662+ }
562663 }
563- throw new InvalidOperationException ( SR . KeyedServicesNotSupported ) ;
564664 }
565- // Try non keyed service
566- return serviceProvider . GetService ( parameterInfo . ParameterType ) ;
567- }
568665
569- private static bool IsService ( IServiceProviderIsService serviceProviderIsService , ParameterInfo parameterInfo )
570- {
571- // Handle keyed service
572- if ( TryGetServiceKey ( parameterInfo , out object ? key ) )
666+ public bool IsService ( IServiceProviderIsService serviceProviderIsService , int parameterIndex )
573667 {
574- if ( serviceProviderIsService is IServiceProviderIsKeyedService serviceProviderIsKeyedService )
668+ ParameterInfo parameterInfo = Parameters [ parameterIndex ] ;
669+
670+ // Handle keyed service
671+ object ? key = _parameterKeys ? [ parameterIndex ] ;
672+ if ( key is not null )
575673 {
576- return serviceProviderIsKeyedService . IsKeyedService ( parameterInfo . ParameterType , key ) ;
674+ if ( serviceProviderIsService is IServiceProviderIsKeyedService serviceProviderIsKeyedService )
675+ {
676+ return serviceProviderIsKeyedService . IsKeyedService ( parameterInfo . ParameterType , key ) ;
677+ }
678+
679+ throw new InvalidOperationException ( SR . KeyedServicesNotSupported ) ;
577680 }
578- throw new InvalidOperationException ( SR . KeyedServicesNotSupported ) ;
681+
682+ // Use non-keyed service
683+ return serviceProviderIsService . IsService ( parameterInfo . ParameterType ) ;
579684 }
580- // Try non keyed service
581- return serviceProviderIsService . IsService ( parameterInfo . ParameterType ) ;
582- }
583685
584- private static bool TryGetServiceKey ( ParameterInfo parameterInfo , out object ? key )
585- {
586- foreach ( var attribute in parameterInfo . GetCustomAttributes < FromKeyedServicesAttribute > ( false ) )
686+ public object ? GetService ( IServiceProvider serviceProvider , int parameterIndex )
587687 {
588- key = attribute . Key ;
589- return true ;
688+ ParameterInfo parameterInfo = Parameters [ parameterIndex ] ;
689+
690+ // Handle keyed service
691+ object ? key = _parameterKeys ? [ parameterIndex ] ;
692+ if ( key is not null )
693+ {
694+ if ( serviceProvider is IKeyedServiceProvider keyedServiceProvider )
695+ {
696+ return keyedServiceProvider . GetKeyedService ( parameterInfo . ParameterType , key ) ;
697+ }
698+
699+ throw new InvalidOperationException ( SR . KeyedServicesNotSupported ) ;
700+ }
701+
702+ // Use non-keyed service
703+ return serviceProvider . GetService ( parameterInfo . ParameterType ) ;
590704 }
591- key = null ;
592- return false ;
593705 }
594706
595707 private readonly struct ConstructorMatcher
596708 {
597- private readonly ConstructorInfo _constructor ;
598- private readonly ParameterInfo [ ] _parameters ;
709+ private readonly ConstructorInfoEx _constructor ;
599710 private readonly object ? [ ] _parameterValues ;
600711
601- public ConstructorMatcher ( ConstructorInfo constructor )
712+ public ConstructorMatcher ( ConstructorInfoEx constructor )
602713 {
603714 _constructor = constructor ;
604- _parameters = _constructor . GetParameters ( ) ;
605- _parameterValues = new object ? [ _parameters . Length ] ;
715+ _parameterValues = new object [ constructor . Parameters . Length ] ;
606716 }
607717
608718 public int Match ( object [ ] givenParameters , IServiceProviderIsService serviceProviderIsService )
@@ -612,10 +722,10 @@ public int Match(object[] givenParameters, IServiceProviderIsService serviceProv
612722 Type ? givenType = givenParameters [ givenIndex ] ? . GetType ( ) ;
613723 bool givenMatched = false ;
614724
615- for ( int applyIndex = 0 ; applyIndex < _parameters . Length ; applyIndex ++ )
725+ for ( int applyIndex = 0 ; applyIndex < _constructor . Parameters . Length ; applyIndex ++ )
616726 {
617727 if ( _parameterValues [ applyIndex ] == null &&
618- _parameters [ applyIndex ] . ParameterType . IsAssignableFrom ( givenType ) )
728+ _constructor . Parameters [ applyIndex ] . ParameterType . IsAssignableFrom ( givenType ) )
619729 {
620730 givenMatched = true ;
621731 _parameterValues [ applyIndex ] = givenParameters [ givenIndex ] ;
@@ -630,12 +740,12 @@ public int Match(object[] givenParameters, IServiceProviderIsService serviceProv
630740 }
631741
632742 // confirms the rest of ctor arguments match either as a parameter with a default value or as a service registered
633- for ( int i = 0 ; i < _parameters . Length ; i ++ )
743+ for ( int i = 0 ; i < _constructor . Parameters . Length ; i ++ )
634744 {
635745 if ( _parameterValues [ i ] == null &&
636- ! IsService ( serviceProviderIsService , _parameters [ i ] ) )
746+ ! _constructor . IsService ( serviceProviderIsService , i ) )
637747 {
638- if ( ParameterDefaultValue . TryGetDefaultValue ( _parameters [ i ] , out object ? defaultValue ) )
748+ if ( ParameterDefaultValue . TryGetDefaultValue ( _constructor . Parameters [ i ] , out object ? defaultValue ) )
639749 {
640750 _parameterValues [ i ] = defaultValue ;
641751 }
@@ -646,21 +756,21 @@ public int Match(object[] givenParameters, IServiceProviderIsService serviceProv
646756 }
647757 }
648758
649- return _parameters . Length ;
759+ return _constructor . Parameters . Length ;
650760 }
651761
652762 public object CreateInstance ( IServiceProvider provider )
653763 {
654- for ( int index = 0 ; index < _parameters . Length ; index ++ )
764+ for ( int index = 0 ; index < _constructor . Parameters . Length ; index ++ )
655765 {
656766 if ( _parameterValues [ index ] == null )
657767 {
658- object ? value = GetService ( provider , _parameters [ index ] ) ;
768+ object ? value = _constructor . GetService ( provider , index ) ;
659769 if ( value == null )
660770 {
661- if ( ! ParameterDefaultValue . TryGetDefaultValue ( _parameters [ index ] , out object ? defaultValue ) )
771+ if ( ! ParameterDefaultValue . TryGetDefaultValue ( _constructor . Parameters [ index ] , out object ? defaultValue ) )
662772 {
663- throw new InvalidOperationException ( SR . Format ( SR . UnableToResolveService , _parameters [ index ] . ParameterType , _constructor . DeclaringType ) ) ;
773+ throw new InvalidOperationException ( SR . Format ( SR . UnableToResolveService , _constructor . Parameters [ index ] . ParameterType , _constructor . Info . DeclaringType ) ) ;
664774 }
665775 else
666776 {
@@ -677,7 +787,7 @@ public object CreateInstance(IServiceProvider provider)
677787#if NETFRAMEWORK || NETSTANDARD2_0
678788 try
679789 {
680- return _constructor . Invoke ( _parameterValues ) ;
790+ return _constructor . Info . Invoke ( _parameterValues ) ;
681791 }
682792 catch ( TargetInvocationException ex ) when ( ex . InnerException != null )
683793 {
@@ -686,13 +796,13 @@ public object CreateInstance(IServiceProvider provider)
686796 throw ;
687797 }
688798#else
689- return _constructor . Invoke ( BindingFlags . DoNotWrapExceptions , binder : null , parameters : _parameterValues , culture : null ) ;
799+ return _constructor . Info . Invoke ( BindingFlags . DoNotWrapExceptions , binder : null , parameters : _parameterValues , culture : null ) ;
690800#endif
691801 }
692802
693803 public void MapParameters ( int ? [ ] parameterMap , object [ ] givenParameters )
694804 {
695- for ( int i = 0 ; i < _parameters . Length ; i ++ )
805+ for ( int i = 0 ; i < _constructor . Parameters . Length ; i ++ )
696806 {
697807 if ( parameterMap [ i ] != null )
698808 {
@@ -974,5 +1084,20 @@ private static object ReflectionFactoryCanonical(
9741084 return constructor . Invoke ( BindingFlags . DoNotWrapExceptions , binder : null , constructorArguments , culture : null ) ;
9751085 }
9761086#endif // NET8_0_OR_GREATER
1087+
1088+ #if NETCOREAPP
1089+ internal static class ActivatorUtilitiesUpdateHandler
1090+ {
1091+ public static void ClearCache ( Type [ ] ? _ )
1092+ {
1093+ // Ignore the Type[] argument; just clear the caches.
1094+ s_constructorInfos . Clear ( ) ;
1095+ if ( s_collectibleConstructorInfos . IsValueCreated )
1096+ {
1097+ s_collectibleConstructorInfos . Value . Clear ( ) ;
1098+ }
1099+ }
1100+ }
1101+ #endif
9771102 }
9781103}
0 commit comments