2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
4
using System ;
5
+ using System . Collections . Concurrent ;
5
6
using System . Diagnostics ;
6
7
using System . Diagnostics . CodeAnalysis ;
7
8
using System . Linq . Expressions ;
10
11
using System . Runtime . ExceptionServices ;
11
12
using Microsoft . Extensions . Internal ;
12
13
14
+ #if NETCOREAPP
15
+ [ assembly: System . Reflection . Metadata . MetadataUpdateHandler ( typeof ( Microsoft . Extensions . DependencyInjection . ActivatorUtilities . ActivatorUtilitiesUpdateHandler ) ) ]
16
+ #endif
17
+
13
18
namespace Microsoft . Extensions . DependencyInjection
14
19
{
15
20
/// <summary>
16
21
/// Helper code for the various activator services.
17
22
/// </summary>
18
23
public static class ActivatorUtilities
19
24
{
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
+
20
33
#if NET8_0_OR_GREATER
21
34
// Maximum number of fixed arguments for ConstructorInvoker.Invoke(arg1, etc).
22
35
private const int FixedArgumentThreshold = 4 ;
@@ -47,6 +60,17 @@ public static object CreateInstance(
47
60
throw new InvalidOperationException ( SR . CannotCreateAbstractClasses ) ;
48
61
}
49
62
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 ;
50
74
IServiceProviderIsService ? serviceProviderIsService = provider . GetService < IServiceProviderIsService > ( ) ;
51
75
// if container supports using IServiceProviderIsService, we try to find the longest ctor that
52
76
// (a) matches all parameters given to CreateInstance
@@ -61,10 +85,11 @@ public static object CreateInstance(
61
85
ConstructorMatcher bestMatcher = default ;
62
86
bool multipleBestLengthFound = false ;
63
87
64
- foreach ( ConstructorInfo ? constructor in instanceType . GetConstructors ( ) )
88
+ for ( int i = 0 ; i < constructors . Length ; i ++ )
65
89
{
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 ;
68
93
int length = matcher . Match ( parameters , serviceProviderIsService ) ;
69
94
70
95
if ( isPreferred )
@@ -105,18 +130,79 @@ public static object CreateInstance(
105
130
}
106
131
}
107
132
108
- Type ? [ ] argumentTypes = new Type [ parameters . Length ] ;
109
- for ( int i = 0 ; i < argumentTypes . Length ; i ++ )
133
+ Type ? [ ] argumentTypes ;
134
+ if ( parameters . Length == 0 )
110
135
{
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
+ }
112
145
}
113
146
114
147
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 ) ;
116
163
constructorMatcher . MapParameters ( parameterMap , parameters ) ;
117
164
return constructorMatcher . CreateInstance ( provider ) ;
118
165
}
119
166
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
+
120
206
/// <summary>
121
207
/// Create a delegate that will instantiate a type with constructor arguments provided directly
122
208
/// and/or from an <see cref="IServiceProvider"/>.
@@ -551,58 +637,82 @@ private static bool TryCreateParameterMap(ParameterInfo[] constructorParameters,
551
637
return true ;
552
638
}
553
639
554
- private static object ? GetService ( IServiceProvider serviceProvider , ParameterInfo parameterInfo )
640
+ private sealed class ConstructorInfoEx
555
641
{
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 )
558
648
{
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 ++ )
560
654
{
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
+ }
562
663
}
563
- throw new InvalidOperationException ( SR . KeyedServicesNotSupported ) ;
564
664
}
565
- // Try non keyed service
566
- return serviceProvider . GetService ( parameterInfo . ParameterType ) ;
567
- }
568
665
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 )
573
667
{
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 )
575
673
{
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 ) ;
577
680
}
578
- throw new InvalidOperationException ( SR . KeyedServicesNotSupported ) ;
681
+
682
+ // Use non-keyed service
683
+ return serviceProviderIsService . IsService ( parameterInfo . ParameterType ) ;
579
684
}
580
- // Try non keyed service
581
- return serviceProviderIsService . IsService ( parameterInfo . ParameterType ) ;
582
- }
583
685
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 )
587
687
{
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 ) ;
590
704
}
591
- key = null ;
592
- return false ;
593
705
}
594
706
595
707
private readonly struct ConstructorMatcher
596
708
{
597
- private readonly ConstructorInfo _constructor ;
598
- private readonly ParameterInfo [ ] _parameters ;
709
+ private readonly ConstructorInfoEx _constructor ;
599
710
private readonly object ? [ ] _parameterValues ;
600
711
601
- public ConstructorMatcher ( ConstructorInfo constructor )
712
+ public ConstructorMatcher ( ConstructorInfoEx constructor )
602
713
{
603
714
_constructor = constructor ;
604
- _parameters = _constructor . GetParameters ( ) ;
605
- _parameterValues = new object ? [ _parameters . Length ] ;
715
+ _parameterValues = new object [ constructor . Parameters . Length ] ;
606
716
}
607
717
608
718
public int Match ( object [ ] givenParameters , IServiceProviderIsService serviceProviderIsService )
@@ -612,10 +722,10 @@ public int Match(object[] givenParameters, IServiceProviderIsService serviceProv
612
722
Type ? givenType = givenParameters [ givenIndex ] ? . GetType ( ) ;
613
723
bool givenMatched = false ;
614
724
615
- for ( int applyIndex = 0 ; applyIndex < _parameters . Length ; applyIndex ++ )
725
+ for ( int applyIndex = 0 ; applyIndex < _constructor . Parameters . Length ; applyIndex ++ )
616
726
{
617
727
if ( _parameterValues [ applyIndex ] == null &&
618
- _parameters [ applyIndex ] . ParameterType . IsAssignableFrom ( givenType ) )
728
+ _constructor . Parameters [ applyIndex ] . ParameterType . IsAssignableFrom ( givenType ) )
619
729
{
620
730
givenMatched = true ;
621
731
_parameterValues [ applyIndex ] = givenParameters [ givenIndex ] ;
@@ -630,12 +740,12 @@ public int Match(object[] givenParameters, IServiceProviderIsService serviceProv
630
740
}
631
741
632
742
// 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 ++ )
634
744
{
635
745
if ( _parameterValues [ i ] == null &&
636
- ! IsService ( serviceProviderIsService , _parameters [ i ] ) )
746
+ ! _constructor . IsService ( serviceProviderIsService , i ) )
637
747
{
638
- if ( ParameterDefaultValue . TryGetDefaultValue ( _parameters [ i ] , out object ? defaultValue ) )
748
+ if ( ParameterDefaultValue . TryGetDefaultValue ( _constructor . Parameters [ i ] , out object ? defaultValue ) )
639
749
{
640
750
_parameterValues [ i ] = defaultValue ;
641
751
}
@@ -646,21 +756,21 @@ public int Match(object[] givenParameters, IServiceProviderIsService serviceProv
646
756
}
647
757
}
648
758
649
- return _parameters . Length ;
759
+ return _constructor . Parameters . Length ;
650
760
}
651
761
652
762
public object CreateInstance ( IServiceProvider provider )
653
763
{
654
- for ( int index = 0 ; index < _parameters . Length ; index ++ )
764
+ for ( int index = 0 ; index < _constructor . Parameters . Length ; index ++ )
655
765
{
656
766
if ( _parameterValues [ index ] == null )
657
767
{
658
- object ? value = GetService ( provider , _parameters [ index ] ) ;
768
+ object ? value = _constructor . GetService ( provider , index ) ;
659
769
if ( value == null )
660
770
{
661
- if ( ! ParameterDefaultValue . TryGetDefaultValue ( _parameters [ index ] , out object ? defaultValue ) )
771
+ if ( ! ParameterDefaultValue . TryGetDefaultValue ( _constructor . Parameters [ index ] , out object ? defaultValue ) )
662
772
{
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 ) ) ;
664
774
}
665
775
else
666
776
{
@@ -677,7 +787,7 @@ public object CreateInstance(IServiceProvider provider)
677
787
#if NETFRAMEWORK || NETSTANDARD2_0
678
788
try
679
789
{
680
- return _constructor . Invoke ( _parameterValues ) ;
790
+ return _constructor . Info . Invoke ( _parameterValues ) ;
681
791
}
682
792
catch ( TargetInvocationException ex ) when ( ex . InnerException != null )
683
793
{
@@ -686,13 +796,13 @@ public object CreateInstance(IServiceProvider provider)
686
796
throw ;
687
797
}
688
798
#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 ) ;
690
800
#endif
691
801
}
692
802
693
803
public void MapParameters ( int ? [ ] parameterMap , object [ ] givenParameters )
694
804
{
695
- for ( int i = 0 ; i < _parameters . Length ; i ++ )
805
+ for ( int i = 0 ; i < _constructor . Parameters . Length ; i ++ )
696
806
{
697
807
if ( parameterMap [ i ] != null )
698
808
{
@@ -974,5 +1084,20 @@ private static object ReflectionFactoryCanonical(
974
1084
return constructor . Invoke ( BindingFlags . DoNotWrapExceptions , binder : null , constructorArguments , culture : null ) ;
975
1085
}
976
1086
#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
977
1102
}
978
1103
}
0 commit comments