/
ActivatorUtilities.cs
185 lines (161 loc) · 7.16 KB
/
ActivatorUtilities.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable enable
using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.ExceptionServices;
namespace Microsoft.Extensions.Internal;
internal static class ActivatorUtilities
{
private const DynamicallyAccessedMemberTypes ActivatorAccessibility = DynamicallyAccessedMemberTypes.PublicConstructors;
/// <summary>
/// Instantiate a type with constructor arguments provided directly and/or from an <see cref="IServiceProvider"/>.
/// </summary>
/// <param name="provider">The service provider used to resolve dependencies</param>
/// <param name="instanceType">The type to activate</param>
/// <param name="parameters">Constructor arguments not provided by the <paramref name="provider"/>.</param>
/// <returns>An activated object of type instanceType</returns>
public static object CreateInstance(
IServiceProvider provider,
[DynamicallyAccessedMembers(ActivatorAccessibility)] Type instanceType,
params object[] parameters)
{
var bestLength = -1;
ConstructorMatcher bestMatcher = default;
if (!instanceType.IsAbstract)
{
foreach (var constructor in instanceType.GetConstructors())
{
var matcher = new ConstructorMatcher(constructor);
var length = matcher.Match(parameters);
if (bestLength < length)
{
bestLength = length;
bestMatcher = matcher;
}
}
}
if (bestLength == -1)
{
var message = $"A suitable constructor for type '{instanceType}' could not be located. Ensure the type is concrete and services are registered for all parameters of a public constructor.";
throw new InvalidOperationException(message);
}
return bestMatcher.CreateInstance(provider);
}
/// <summary>
/// Instantiate a type with constructor arguments provided directly and/or from an <see cref="IServiceProvider"/>.
/// </summary>
/// <typeparam name="T">The type to activate</typeparam>
/// <param name="provider">The service provider used to resolve dependencies</param>
/// <param name="parameters">Constructor arguments not provided by the <paramref name="provider"/>.</param>
/// <returns>An activated object of type T</returns>
public static T CreateInstance<
[DynamicallyAccessedMembers(ActivatorAccessibility)] T
>(IServiceProvider provider, params object[] parameters)
{
return (T)CreateInstance(provider, typeof(T), parameters);
}
/// <summary>
/// Retrieve an instance of the given type from the service provider. If one is not found then instantiate it directly.
/// </summary>
/// <typeparam name="T">The type of the service</typeparam>
/// <param name="provider">The service provider used to resolve dependencies</param>
/// <returns>The resolved service or created instance</returns>
public static T GetServiceOrCreateInstance<
[DynamicallyAccessedMembers(ActivatorAccessibility)] T
>(IServiceProvider provider)
{
return (T)GetServiceOrCreateInstance(provider, typeof(T));
}
/// <summary>
/// Retrieve an instance of the given type from the service provider. If one is not found then instantiate it directly.
/// </summary>
/// <param name="provider">The service provider</param>
/// <param name="type">The type of the service</param>
/// <returns>The resolved service or created instance</returns>
public static object GetServiceOrCreateInstance(
IServiceProvider provider,
[DynamicallyAccessedMembers(ActivatorAccessibility)] Type type)
{
return provider.GetService(type) ?? CreateInstance(provider, type);
}
private struct ConstructorMatcher
{
private readonly ConstructorInfo _constructor;
private readonly ParameterInfo[] _parameters;
private readonly object?[] _parameterValues;
public ConstructorMatcher(ConstructorInfo constructor)
{
_constructor = constructor;
_parameters = _constructor.GetParameters();
_parameterValues = new object?[_parameters.Length];
}
public int Match(object[] givenParameters)
{
var applyIndexStart = 0;
var applyExactLength = 0;
for (var givenIndex = 0; givenIndex != givenParameters.Length; givenIndex++)
{
var givenType = givenParameters[givenIndex]?.GetType();
var givenMatched = false;
for (var applyIndex = applyIndexStart; givenMatched == false && applyIndex != _parameters.Length; ++applyIndex)
{
if (_parameterValues[applyIndex] == null &&
_parameters[applyIndex].ParameterType.IsAssignableFrom(givenType))
{
givenMatched = true;
_parameterValues[applyIndex] = givenParameters[givenIndex];
if (applyIndexStart == applyIndex)
{
applyIndexStart++;
if (applyIndex == givenIndex)
{
applyExactLength = applyIndex;
}
}
}
}
if (givenMatched == false)
{
return -1;
}
}
return applyExactLength;
}
public object CreateInstance(IServiceProvider provider)
{
for (var index = 0; index < _parameters.Length; index++)
{
var parameter = _parameters[index];
if (_parameterValues[index] is null)
{
var value = provider.GetService(parameter.ParameterType);
if (value is null)
{
if (!ParameterDefaultValue.TryGetDefaultValue(parameter, out var defaultValue))
{
throw new InvalidOperationException($"Unable to resolve service for type '{_parameters[index].ParameterType}' while attempting to activate '{_constructor.DeclaringType}'.");
}
value = defaultValue;
}
_parameterValues[index] = value;
}
}
#if NETCOREAPP
return _constructor.Invoke(BindingFlags.DoNotWrapExceptions, binder: null, parameters: _parameterValues, culture: null);
#else
try
{
return _constructor.Invoke(_parameterValues);
}
catch (TargetInvocationException ex) when (ex.InnerException != null)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
// The above line will always throw, but the compiler requires we throw explicitly.
throw;
}
#endif
}
}
}