commit
b0441192c3
|
|
@ -0,0 +1,429 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
|
||||||
|
#if ActivatorUtilities_In_DependencyInjection
|
||||||
|
using Microsoft.Extensions.Internal;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
#else
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Helper code for the various activator services.
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
#if ActivatorUtilities_In_DependencyInjection
|
||||||
|
public
|
||||||
|
#else
|
||||||
|
// Do not take a dependency on this class unless you are explicitly trying to avoid taking a
|
||||||
|
// dependency on Microsoft.AspNetCore.DependencyInjection.Abstractions.
|
||||||
|
internal
|
||||||
|
#endif
|
||||||
|
static class ActivatorUtilities
|
||||||
|
{
|
||||||
|
private static readonly MethodInfo GetServiceInfo =
|
||||||
|
GetMethodInfo<Func<IServiceProvider, Type, Type, bool, object>>((sp, t, r, c) => GetService(sp, t, r, c));
|
||||||
|
|
||||||
|
/// <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, Type instanceType, params object[] parameters)
|
||||||
|
{
|
||||||
|
int bestLength = -1;
|
||||||
|
var seenPreferred = false;
|
||||||
|
|
||||||
|
ConstructorMatcher bestMatcher = null;
|
||||||
|
|
||||||
|
if (!instanceType.GetTypeInfo().IsAbstract)
|
||||||
|
{
|
||||||
|
foreach (var constructor in instanceType
|
||||||
|
.GetTypeInfo()
|
||||||
|
.DeclaredConstructors
|
||||||
|
.Where(c => !c.IsStatic && c.IsPublic))
|
||||||
|
{
|
||||||
|
var matcher = new ConstructorMatcher(constructor);
|
||||||
|
var isPreferred = constructor.IsDefined(typeof(ActivatorUtilitiesConstructorAttribute), false);
|
||||||
|
var length = matcher.Match(parameters);
|
||||||
|
|
||||||
|
if (isPreferred)
|
||||||
|
{
|
||||||
|
if (seenPreferred)
|
||||||
|
{
|
||||||
|
ThrowMultipleCtorsMarkedWithAttributeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (length == -1)
|
||||||
|
{
|
||||||
|
ThrowMarkedCtorDoesNotTakeAllProvidedArguments();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isPreferred || bestLength < length)
|
||||||
|
{
|
||||||
|
bestLength = length;
|
||||||
|
bestMatcher = matcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
seenPreferred |= isPreferred;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestMatcher == null)
|
||||||
|
{
|
||||||
|
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>
|
||||||
|
/// Create a delegate that will instantiate a type with constructor arguments provided directly
|
||||||
|
/// and/or from an <see cref="IServiceProvider"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instanceType">The type to activate</param>
|
||||||
|
/// <param name="argumentTypes">
|
||||||
|
/// The types of objects, in order, that will be passed to the returned function as its second parameter
|
||||||
|
/// </param>
|
||||||
|
/// <returns>
|
||||||
|
/// A factory that will instantiate instanceType using an <see cref="IServiceProvider"/>
|
||||||
|
/// and an argument array containing objects matching the types defined in argumentTypes
|
||||||
|
/// </returns>
|
||||||
|
public static ObjectFactory CreateFactory(Type instanceType, Type[] argumentTypes)
|
||||||
|
{
|
||||||
|
FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap);
|
||||||
|
|
||||||
|
var provider = Expression.Parameter(typeof(IServiceProvider), "provider");
|
||||||
|
var argumentArray = Expression.Parameter(typeof(object[]), "argumentArray");
|
||||||
|
var factoryExpressionBody = BuildFactoryExpression(constructor, parameterMap, provider, argumentArray);
|
||||||
|
|
||||||
|
var factoryLamda = Expression.Lambda<Func<IServiceProvider, object[], object>>(
|
||||||
|
factoryExpressionBody, provider, argumentArray);
|
||||||
|
|
||||||
|
var result = factoryLamda.Compile();
|
||||||
|
return result.Invoke;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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<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<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, Type type)
|
||||||
|
{
|
||||||
|
return provider.GetService(type) ?? CreateInstance(provider, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodInfo GetMethodInfo<T>(Expression<T> expr)
|
||||||
|
{
|
||||||
|
var mc = (MethodCallExpression)expr.Body;
|
||||||
|
return mc.Method;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
|
||||||
|
{
|
||||||
|
var service = sp.GetService(type);
|
||||||
|
if (service == null && !isDefaultParameterRequired)
|
||||||
|
{
|
||||||
|
var message = $"Unable to resolve service for type '{type}' while attempting to activate '{requiredBy}'.";
|
||||||
|
throw new InvalidOperationException(message);
|
||||||
|
}
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Expression BuildFactoryExpression(
|
||||||
|
ConstructorInfo constructor,
|
||||||
|
int?[] parameterMap,
|
||||||
|
Expression serviceProvider,
|
||||||
|
Expression factoryArgumentArray)
|
||||||
|
{
|
||||||
|
var constructorParameters = constructor.GetParameters();
|
||||||
|
var constructorArguments = new Expression[constructorParameters.Length];
|
||||||
|
|
||||||
|
for (var i = 0; i < constructorParameters.Length; i++)
|
||||||
|
{
|
||||||
|
var constructorParameter = constructorParameters[i];
|
||||||
|
var parameterType = constructorParameter.ParameterType;
|
||||||
|
var hasDefaultValue = ParameterDefaultValue.TryGetDefaultValue(constructorParameter, out var defaultValue);
|
||||||
|
|
||||||
|
if (parameterMap[i] != null)
|
||||||
|
{
|
||||||
|
constructorArguments[i] = Expression.ArrayAccess(factoryArgumentArray, Expression.Constant(parameterMap[i]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var parameterTypeExpression = new Expression[] { serviceProvider,
|
||||||
|
Expression.Constant(parameterType, typeof(Type)),
|
||||||
|
Expression.Constant(constructor.DeclaringType, typeof(Type)),
|
||||||
|
Expression.Constant(hasDefaultValue) };
|
||||||
|
constructorArguments[i] = Expression.Call(GetServiceInfo, parameterTypeExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support optional constructor arguments by passing in the default value
|
||||||
|
// when the argument would otherwise be null.
|
||||||
|
if (hasDefaultValue)
|
||||||
|
{
|
||||||
|
var defaultValueExpression = Expression.Constant(defaultValue);
|
||||||
|
constructorArguments[i] = Expression.Coalesce(constructorArguments[i], defaultValueExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructorArguments[i] = Expression.Convert(constructorArguments[i], parameterType);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Expression.New(constructor, constructorArguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void FindApplicableConstructor(
|
||||||
|
Type instanceType,
|
||||||
|
Type[] argumentTypes,
|
||||||
|
out ConstructorInfo matchingConstructor,
|
||||||
|
out int?[] parameterMap)
|
||||||
|
{
|
||||||
|
matchingConstructor = null;
|
||||||
|
parameterMap = null;
|
||||||
|
|
||||||
|
if (!TryFindPreferredConstructor(instanceType, argumentTypes, ref matchingConstructor, ref parameterMap) &&
|
||||||
|
!TryFindMatchingConstructor(instanceType, argumentTypes, ref matchingConstructor, ref parameterMap))
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tries to find constructor based on provided argument types
|
||||||
|
private static bool TryFindMatchingConstructor(
|
||||||
|
Type instanceType,
|
||||||
|
Type[] argumentTypes,
|
||||||
|
ref ConstructorInfo matchingConstructor,
|
||||||
|
ref int?[] parameterMap)
|
||||||
|
{
|
||||||
|
foreach (var constructor in instanceType.GetTypeInfo().DeclaredConstructors)
|
||||||
|
{
|
||||||
|
if (constructor.IsStatic || !constructor.IsPublic)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryCreateParameterMap(constructor.GetParameters(), argumentTypes, out int?[] tempParameterMap))
|
||||||
|
{
|
||||||
|
if (matchingConstructor != null)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Multiple constructors accepting all given argument types have been found in type '{instanceType}'. There should only be one applicable constructor.");
|
||||||
|
}
|
||||||
|
|
||||||
|
matchingConstructor = constructor;
|
||||||
|
parameterMap = tempParameterMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingConstructor != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tries to find constructor marked with ActivatorUtilitiesConstructorAttribute
|
||||||
|
private static bool TryFindPreferredConstructor(
|
||||||
|
Type instanceType,
|
||||||
|
Type[] argumentTypes,
|
||||||
|
ref ConstructorInfo matchingConstructor,
|
||||||
|
ref int?[] parameterMap)
|
||||||
|
{
|
||||||
|
var seenPreferred = false;
|
||||||
|
foreach (var constructor in instanceType.GetTypeInfo().DeclaredConstructors)
|
||||||
|
{
|
||||||
|
if (constructor.IsStatic || !constructor.IsPublic)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constructor.IsDefined(typeof(ActivatorUtilitiesConstructorAttribute), false))
|
||||||
|
{
|
||||||
|
if (seenPreferred)
|
||||||
|
{
|
||||||
|
ThrowMultipleCtorsMarkedWithAttributeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryCreateParameterMap(constructor.GetParameters(), argumentTypes, out int?[] tempParameterMap))
|
||||||
|
{
|
||||||
|
ThrowMarkedCtorDoesNotTakeAllProvidedArguments();
|
||||||
|
}
|
||||||
|
|
||||||
|
matchingConstructor = constructor;
|
||||||
|
parameterMap = tempParameterMap;
|
||||||
|
seenPreferred = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingConstructor != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an injective parameterMap from givenParameterTypes to assignable constructorParameters.
|
||||||
|
// Returns true if each given parameter type is assignable to a unique; otherwise, false.
|
||||||
|
private static bool TryCreateParameterMap(ParameterInfo[] constructorParameters, Type[] argumentTypes, out int?[] parameterMap)
|
||||||
|
{
|
||||||
|
parameterMap = new int?[constructorParameters.Length];
|
||||||
|
|
||||||
|
for (var i = 0; i < argumentTypes.Length; i++)
|
||||||
|
{
|
||||||
|
var foundMatch = false;
|
||||||
|
var givenParameter = argumentTypes[i].GetTypeInfo();
|
||||||
|
|
||||||
|
for (var j = 0; j < constructorParameters.Length; j++)
|
||||||
|
{
|
||||||
|
if (parameterMap[j] != null)
|
||||||
|
{
|
||||||
|
// This ctor parameter has already been matched
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constructorParameters[j].ParameterType.GetTypeInfo().IsAssignableFrom(givenParameter))
|
||||||
|
{
|
||||||
|
foundMatch = true;
|
||||||
|
parameterMap[j] = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundMatch)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ConstructorMatcher
|
||||||
|
{
|
||||||
|
private readonly ConstructorInfo _constructor;
|
||||||
|
private readonly ParameterInfo[] _parameters;
|
||||||
|
private readonly object[] _parameterValues;
|
||||||
|
private readonly bool[] _parameterValuesSet;
|
||||||
|
|
||||||
|
public ConstructorMatcher(ConstructorInfo constructor)
|
||||||
|
{
|
||||||
|
_constructor = constructor;
|
||||||
|
_parameters = _constructor.GetParameters();
|
||||||
|
_parameterValuesSet = new bool[_parameters.Length];
|
||||||
|
_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().GetTypeInfo();
|
||||||
|
var givenMatched = false;
|
||||||
|
|
||||||
|
for (var applyIndex = applyIndexStart; givenMatched == false && applyIndex != _parameters.Length; ++applyIndex)
|
||||||
|
{
|
||||||
|
if (_parameterValuesSet[applyIndex] == false &&
|
||||||
|
_parameters[applyIndex].ParameterType.GetTypeInfo().IsAssignableFrom(givenType))
|
||||||
|
{
|
||||||
|
givenMatched = true;
|
||||||
|
_parameterValuesSet[applyIndex] = 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++)
|
||||||
|
{
|
||||||
|
if (_parameterValuesSet[index] == false)
|
||||||
|
{
|
||||||
|
var value = provider.GetService(_parameters[index].ParameterType);
|
||||||
|
if (value == null)
|
||||||
|
{
|
||||||
|
if (!ParameterDefaultValue.TryGetDefaultValue(_parameters[index], out var defaultValue))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Unable to resolve service for type '{_parameters[index].ParameterType}' while attempting to activate '{_constructor.DeclaringType}'.");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_parameterValues[index] = defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_parameterValues[index] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return _constructor.Invoke(_parameterValues);
|
||||||
|
}
|
||||||
|
catch (TargetInvocationException ex)
|
||||||
|
{
|
||||||
|
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
|
||||||
|
// The above line will always throw, but the compiler requires we throw explicitly.
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ThrowMultipleCtorsMarkedWithAttributeException()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Multiple constructors were marked with {nameof(ActivatorUtilitiesConstructorAttribute)}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ThrowMarkedCtorDoesNotTakeAllProvidedArguments()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException($"Constructor marked with {nameof(ActivatorUtilitiesConstructorAttribute)} does not accept all given argument types.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
#if ActivatorUtilities_In_DependencyInjection
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
#else
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Marks the constructor to be used when activating type using <see cref="ActivatorUtilities"/>.
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
#if ActivatorUtilities_In_DependencyInjection
|
||||||
|
public
|
||||||
|
#else
|
||||||
|
// Do not take a dependency on this class unless you are explicitly trying to avoid taking a
|
||||||
|
// dependency on Microsoft.AspNetCore.DependencyInjection.Abstractions.
|
||||||
|
internal
|
||||||
|
#endif
|
||||||
|
class ActivatorUtilitiesConstructorAttribute: Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
#if ActivatorUtilities_In_DependencyInjection
|
||||||
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
#else
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The result of <see cref="ActivatorUtilities.CreateFactory(Type, Type[])"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to get service arguments from.</param>
|
||||||
|
/// <param name="arguments">Additional constructor arguments.</param>
|
||||||
|
/// <returns>The instantiated type.</returns>
|
||||||
|
#if ActivatorUtilities_In_DependencyInjection
|
||||||
|
public
|
||||||
|
#else
|
||||||
|
internal
|
||||||
|
#endif
|
||||||
|
delegate object ObjectFactory(IServiceProvider serviceProvider, object[] arguments);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
<Project>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="$(MSBuildThisFileDirectory)..\ParameterDefaultValue\ParameterDefaultValue.cs">
|
||||||
|
<Pack>true</Pack>
|
||||||
|
<PackagePath>$(ContentTargetFolders)\cs\netstandard1.0\</PackagePath>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
|
@ -0,0 +1,47 @@
|
||||||
|
// Copyright (c) .NET Foundation. All rights reserved.
|
||||||
|
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Microsoft.Extensions.Internal
|
||||||
|
{
|
||||||
|
internal class ParameterDefaultValue
|
||||||
|
{
|
||||||
|
public static bool TryGetDefaultValue(ParameterInfo parameter, out object defaultValue)
|
||||||
|
{
|
||||||
|
bool hasDefaultValue;
|
||||||
|
var tryToGetDefaultValue = true;
|
||||||
|
defaultValue = null;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
hasDefaultValue = parameter.HasDefaultValue;
|
||||||
|
}
|
||||||
|
catch (FormatException) when (parameter.ParameterType == typeof(DateTime))
|
||||||
|
{
|
||||||
|
// Workaround for https://github.com/dotnet/corefx/issues/12338
|
||||||
|
// If HasDefaultValue throws FormatException for DateTime
|
||||||
|
// we expect it to have default value
|
||||||
|
hasDefaultValue = true;
|
||||||
|
tryToGetDefaultValue = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasDefaultValue)
|
||||||
|
{
|
||||||
|
if (tryToGetDefaultValue)
|
||||||
|
{
|
||||||
|
defaultValue = parameter.DefaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for https://github.com/dotnet/corefx/issues/11797
|
||||||
|
if (defaultValue == null && parameter.ParameterType.IsValueType)
|
||||||
|
{
|
||||||
|
defaultValue = Activator.CreateInstance(parameter.ParameterType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasDefaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue