/
githubmirror
/
aspnetcore
Обзор
Документация
Войти
/
githubmirror
/
aspnetcore
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Shared/ParameterBindingMethodCache.cs
743 строки
35 KB
Jiri Cincura ↹
Fix typos in code. (#67428)
02 июл 2026, 13:29
Не верифицирован
02 июл 2026, 13:29
fa8126f
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Concurrent; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Linq.Expressions; using System.Numerics; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using Microsoft.Extensions.Internal; using static Microsoft.AspNetCore.Http.ParameterBindingMethodCache.SharedExpressions; #nullable enable namespace Microsoft.AspNetCore.Http; [RequiresUnreferencedCode("Uses unbounded Reflection to inspect property types.")] internal sealed class ParameterBindingMethodCache { private static readonly MethodInfo ConvertValueTaskMethod = typeof(ParameterBindingMethodCache).GetMethod(nameof(ConvertValueTask), BindingFlags.NonPublic | BindingFlags.Static)!; private static readonly MethodInfo ConvertValueTaskOfNullableResultMethod = typeof(ParameterBindingMethodCache).GetMethod(nameof(ConvertValueTaskOfNullableResult), BindingFlags.NonPublic | BindingFlags.Static)!; private static readonly MethodInfo BindAsyncMethod = typeof(ParameterBindingMethodCache).GetMethod(nameof(BindAsync), BindingFlags.NonPublic | BindingFlags.Static)!; private static readonly MethodInfo UriTryCreateMethod = typeof(Uri).GetMethod(nameof(Uri.TryCreate), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(UriKind), typeof(Uri).MakeByRefType() })!; // Thread-safe singletons for ParameterBindingMethodCache private static readonly Lazy<ParameterBindingMethodCache> _instance = new(() => new ParameterBindingMethodCache()); public static ParameterBindingMethodCache Instance = _instance.Value; private static readonly Lazy<ParameterBindingMethodCache> _nonThrowingInstance = new(() => new ParameterBindingMethodCache(throwOnInvalidMethod: false)); public static ParameterBindingMethodCache NonThrowingInstance = _nonThrowingInstance.Value; // work around https://github.com/dotnet/runtime/issues/81864 by splitting these into a separate class. internal static class SharedExpressions { internal static readonly ParameterExpression TempSourceStringExpr = Expression.Variable(typeof(string), "tempSourceString"); internal static readonly ParameterExpression HttpContextExpr = Expression.Parameter(typeof(HttpContext), "httpContext"); } private readonly MethodInfo _enumTryParseMethod; private readonly bool _throwOnInvalidMethod; // Since this is shared source, the cache won't be shared between RequestDelegateFactory and the ApiDescriptionProvider sadly :( private readonly ConcurrentDictionary<Type, Func<ParameterExpression, Expression, Expression>?> _stringMethodCallCache = new(); private readonly ConcurrentDictionary<Type, (Func<ParameterInfo, Expression>?, int)> _bindAsyncMethodCallCache = new(); private readonly ConcurrentDictionary<Type, (ConstructorInfo?, ConstructorParameter[])> _constructorCache = new(); // If IsDynamicCodeSupported is false, we can't use the static Enum.TryParse<T> since there's no easy way for // this code to generate the specific instantiation for any enums used public ParameterBindingMethodCache(bool throwOnInvalidMethod = true) : this(preferNonGenericEnumParseOverload: !RuntimeFeature.IsDynamicCodeSupported, throwOnInvalidMethod) { } // This is for testing public ParameterBindingMethodCache(bool preferNonGenericEnumParseOverload, bool throwOnInvalidMethod = true) { _enumTryParseMethod = GetEnumTryParseMethod(preferNonGenericEnumParseOverload); _throwOnInvalidMethod = throwOnInvalidMethod; } [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] [RequiresDynamicCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] public bool HasTryParseMethod(Type type) { var nonNullableParameterType = Nullable.GetUnderlyingType(type) ?? type; return FindTryParseMethod(nonNullableParameterType) is not null; } [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] [RequiresDynamicCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] public bool HasBindAsyncMethod(ParameterInfo parameter) => FindBindAsyncMethod(parameter).Expression is not null; [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] [RequiresDynamicCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] public Func<ParameterExpression, Expression, Expression>? FindTryParseMethod(Type type) { // This method is used to find TryParse methods from .NET types using reflection. It's used at app runtime. // Routing analyzers also detect TryParse methods when calculating what types are valid in routes. // Changes here to support new types should be reflected in analyzers. Func<ParameterExpression, Expression, Expression>? Finder(Type type) { MethodInfo? methodInfo; if (TryGetExplicitIParsableTryParseMethod(type, out var explicitIParsableTryParseMethod)) { return (expression, formatProvider) => Expression.Call(explicitIParsableTryParseMethod, TempSourceStringExpr, formatProvider, expression); } if (type.IsEnum) { if (_enumTryParseMethod.IsGenericMethod) { methodInfo = _enumTryParseMethod.MakeGenericMethod(type); return (expression, formatProvider) => Expression.Call(methodInfo!, TempSourceStringExpr, expression); } return (expression, formatProvider) => { var enumAsObject = Expression.Variable(typeof(object), "enumAsObject"); var success = Expression.Variable(typeof(bool), "success"); // object enumAsObject; // bool success; // success = Enum.TryParse(type, tempSourceString, out enumAsObject); // parsedValue = success ? (Type)enumAsObject : default; // return success; return Expression.Block(new[] { success, enumAsObject }, Expression.Assign(success, Expression.Call(_enumTryParseMethod, Expression.Constant(type), TempSourceStringExpr, enumAsObject)), Expression.Assign(expression, Expression.Condition(success, Expression.Convert(enumAsObject, type), Expression.Default(type))), success); }; } if (type == typeof(Uri)) { // UriKind.RelativeOrAbsolute is also used by UriTypeConverter which is used in MVC. return (expression, formatProvider) => Expression.Call( UriTryCreateMethod, TempSourceStringExpr, Expression.Constant(UriKind.RelativeOrAbsolute), expression); } if (TryGetDateTimeTryParseMethod(type, out methodInfo)) { // We generate `DateTimeStyles.AdjustToUniversal | DateTimeStyles.AllowWhiteSpaces ` to // support parsing types into the UTC timezone for DateTime. We don't assume the timezone // on the original value which will cause the parser to set the `Kind` property on the // `DateTime` as `Unspecified` indicating that it was parsed from an ambiguous timezone. // // `DateTimeOffset`s are always in UTC and don't allow specifying an `Unspecific` kind. // For this, we always assume that the original value is already in UTC to avoid resolving // the offset incorrectly depending on the timezone of the machine. We don't bother mapping // it to UTC in this case. In the event that the original timestamp is not in UTC, it's offset // value will be maintained. // // DateOnly and TimeOnly types do not support conversion to Utc so we // default to `DateTimeStyles.AllowWhiteSpaces`. var dateTimeStyles = type switch { Type t when t == typeof(DateTime) => DateTimeStyles.AdjustToUniversal | DateTimeStyles.AllowWhiteSpaces, Type t when t == typeof(DateTimeOffset) => DateTimeStyles.AssumeUniversal | DateTimeStyles.AllowWhiteSpaces, _ => DateTimeStyles.AllowWhiteSpaces }; return (expression, formatProvider) => Expression.Call( methodInfo!, TempSourceStringExpr, formatProvider, Expression.Constant(dateTimeStyles), expression); } if (TryGetNumberStylesTryGetMethod(type, out methodInfo, out var numberStyle)) { return (expression, formatProvider) => Expression.Call( methodInfo!, TempSourceStringExpr, Expression.Constant(numberStyle), formatProvider, expression); } methodInfo = GetStaticMethodFromHierarchy(type, "TryParse", new[] { typeof(string), typeof(IFormatProvider), type.MakeByRefType() }, ValidateReturnType); if (methodInfo is not null) { return (expression, formatProvider) => Expression.Call( methodInfo, TempSourceStringExpr, formatProvider, expression); } methodInfo = GetStaticMethodFromHierarchy(type, "TryParse", new[] { typeof(string), type.MakeByRefType() }, ValidateReturnType); if (methodInfo is not null) { return (expression, formatProvider) => Expression.Call(methodInfo, TempSourceStringExpr, expression); } if (_throwOnInvalidMethod && GetAnyMethodFromHierarchy(type, "TryParse") is MethodInfo invalidMethod) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"TryParse method found on {TypeNameHelper.GetTypeDisplayName(type, fullName: false)} with incorrect format. Must be a static method with format"); stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"bool TryParse(string, IFormatProvider, out {TypeNameHelper.GetTypeDisplayName(type, fullName: false)})"); stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"bool TryParse(string, out {TypeNameHelper.GetTypeDisplayName(type, fullName: false)})"); stringBuilder.AppendLine("but found"); stringBuilder.Append(invalidMethod.IsStatic ? "static " : "not-static "); stringBuilder.Append(invalidMethod.ToString()); throw new InvalidOperationException(stringBuilder.ToString()); } return null; static bool ValidateReturnType(MethodInfo methodInfo) { return methodInfo.ReturnType.Equals(typeof(bool)); } } return _stringMethodCallCache.GetOrAdd(type, Finder); } [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] [RequiresDynamicCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] public (Expression? Expression, int ParamCount) FindBindAsyncMethod(ParameterInfo parameter) { (Func<ParameterInfo, Expression>?, int) Finder(Type nonNullableParameterType) { var hasParameterInfo = true; var methodInfo = GetIBindableFromHttpContextMethod(nonNullableParameterType); if (methodInfo is null) { // There should only be one BindAsync method with these parameters since C# does not allow overloading on return type. methodInfo = GetStaticMethodFromHierarchy(nonNullableParameterType, "BindAsync", new[] { typeof(HttpContext), typeof(ParameterInfo) }, ValidateReturnType); if (methodInfo is null) { hasParameterInfo = false; methodInfo = GetStaticMethodFromHierarchy(nonNullableParameterType, "BindAsync", new[] { typeof(HttpContext) }, ValidateReturnType); } } // We're looking for a method with the following signatures: // public static ValueTask<{type}> BindAsync(HttpContext context, ParameterInfo parameter) // public static ValueTask<Nullable<{type}>> BindAsync(HttpContext context, ParameterInfo parameter) if (methodInfo is not null) { var valueTaskResultType = methodInfo.ReturnType.GetGenericArguments()[0]; // ValueTask<{type}>? if (valueTaskResultType == nonNullableParameterType) { return ((parameter) => { MethodCallExpression typedCall; if (hasParameterInfo) { // parameter is being intentionally shadowed. We never want to use the outer ParameterInfo inside // this Func because the ParameterInfo varies after it's been cached for a given parameter type. typedCall = Expression.Call(methodInfo, HttpContextExpr, Expression.Constant(parameter)); } else { typedCall = Expression.Call(methodInfo, HttpContextExpr); } return Expression.Call(ConvertValueTaskMethod.MakeGenericMethod(nonNullableParameterType), typedCall); }, hasParameterInfo ? 2 : 1); } // ValueTask<Nullable<{type}>>? else if (valueTaskResultType.IsGenericType && valueTaskResultType.GetGenericTypeDefinition() == typeof(Nullable<>) && valueTaskResultType.GetGenericArguments()[0] == nonNullableParameterType) { return ((parameter) => { MethodCallExpression typedCall; if (hasParameterInfo) { // parameter is being intentionally shadowed. We never want to use the outer ParameterInfo inside // this Func because the ParameterInfo varies after it's been cached for a given parameter type. typedCall = Expression.Call(methodInfo, HttpContextExpr, Expression.Constant(parameter)); } else { typedCall = Expression.Call(methodInfo, HttpContextExpr); } return Expression.Call(ConvertValueTaskOfNullableResultMethod.MakeGenericMethod(nonNullableParameterType), typedCall); }, hasParameterInfo ? 2 : 1); } } if (_throwOnInvalidMethod && GetAnyMethodFromHierarchy(nonNullableParameterType, "BindAsync") is MethodInfo invalidBindMethod) { var stringBuilder = new StringBuilder(); stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"BindAsync method found on {TypeNameHelper.GetTypeDisplayName(nonNullableParameterType, fullName: false)} with incorrect format. Must be a static method with format"); stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"ValueTask<{TypeNameHelper.GetTypeDisplayName(nonNullableParameterType, fullName: false)}> BindAsync(HttpContext context, ParameterInfo parameter)"); stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"ValueTask<{TypeNameHelper.GetTypeDisplayName(nonNullableParameterType, fullName: false)}> BindAsync(HttpContext context)"); stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"ValueTask<{TypeNameHelper.GetTypeDisplayName(nonNullableParameterType, fullName: false)}?> BindAsync(HttpContext context, ParameterInfo parameter)"); stringBuilder.AppendLine(CultureInfo.InvariantCulture, $"ValueTask<{TypeNameHelper.GetTypeDisplayName(nonNullableParameterType, fullName: false)}?> BindAsync(HttpContext context)"); stringBuilder.AppendLine("but found"); stringBuilder.Append(invalidBindMethod.IsStatic ? "static " : "not-static"); stringBuilder.Append(invalidBindMethod.ToString()); throw new InvalidOperationException(stringBuilder.ToString()); } return (null, 0); } var nonNullableParameterType = Nullable.GetUnderlyingType(parameter.ParameterType) ?? parameter.ParameterType; var (method, paramCount) = _bindAsyncMethodCallCache.GetOrAdd(nonNullableParameterType, Finder); return (method?.Invoke(parameter), paramCount); static bool ValidateReturnType(MethodInfo methodInfo) { return methodInfo.ReturnType.IsGenericType && methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(ValueTask<>); } } public (ConstructorInfo?, ConstructorParameter[]) FindConstructor(Type type) { static (ConstructorInfo? constructor, ConstructorParameter[] parameters) Finder(Type type) { var constructor = GetConstructor(type); if (constructor is null || constructor.GetParameters().Length == 0) { return (constructor, Array.Empty<ConstructorParameter>()); } var properties = type.GetProperties(); var lookupTable = new Dictionary<ParameterLookupKey, PropertyInfo>(properties.Length); for (var i = 0; i < properties.Length; i++) { lookupTable.Add(new ParameterLookupKey(properties[i].Name, properties[i].PropertyType), properties[i]); } // This behavior diverge from the JSON serialization // since we don't have an attribute, eg. JsonConstructor, // we need to be very restrictive about the ctor // and only accept if the parameterized ctor has // only arguments that we can match (Type and Name) // with a public property. var parameters = constructor.GetParameters(); var parametersWithPropertyInfo = new ConstructorParameter[parameters.Length]; for (var i = 0; i < parameters.Length; i++) { var key = new ParameterLookupKey(parameters[i].Name!, parameters[i].ParameterType); if (!lookupTable.TryGetValue(key, out var property)) { throw new InvalidOperationException( $"The public parameterized constructor must contain only parameters that match the declared public properties for type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}'."); } parametersWithPropertyInfo[i] = new ConstructorParameter(parameters[i], property); } return (constructor, parametersWithPropertyInfo); } return _constructorCache.GetOrAdd(type, Finder); } private static ConstructorInfo? GetConstructor(Type type) { if (type.IsAbstract) { throw new InvalidOperationException($"The abstract type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}' is not supported."); } var constructors = type.GetConstructors(BindingFlags.Public | BindingFlags.Instance); // if only one constructor is declared // we will use it to try match the properties if (constructors.Length == 1) { return constructors[0]; } // We will try to get the parameterless ctor // as priority before visit the others var parameterlessConstructor = constructors.SingleOrDefault(c => c.GetParameters().Length == 0); if (parameterlessConstructor is not null) { return parameterlessConstructor; } // If a parameterized constructors is not found at this point // we will use a default constructor that is always available // for value types. if (type.IsValueType) { return null; } // We don't have an attribute, similar to JsonConstructor, to // disambiguate ctors, so, we will throw if more than one // ctor is defined without a parameterless constructor. // Eg.: // public class X // { // public X(int foo) // public X(int foo, int bar) // ... // } if (parameterlessConstructor is null && constructors.Length > 1) { throw new InvalidOperationException($"Only a single public parameterized constructor is allowed for type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}'."); } throw new InvalidOperationException($"No public parameterless constructor found for type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}'."); } [RequiresDynamicCode("MakeGenericMethod is possible used with ValueTypes and isn't compatible with AOT.")] private static MethodInfo? GetIBindableFromHttpContextMethod(Type type) { // Check if parameter is bindable via static abstract method on IBindableFromHttpContext<TSelf> foreach (var i in type.GetInterfaces()) { if (i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IBindableFromHttpContext<>) && i.GetGenericArguments()[0] == type) { return BindAsyncMethod.MakeGenericMethod(type); } } return null; } private static ValueTask<TValue?> BindAsync<TValue>(HttpContext httpContext, ParameterInfo parameter) where TValue : class?, IBindableFromHttpContext<TValue> { return TValue.BindAsync(httpContext, parameter); } [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] private static bool TryGetExplicitIParsableTryParseMethod(Type type, out MethodInfo methodInfo) { // Nested types by default use + as the delimiter between the containing type and the // inner type. However when doing a method search this '+' symbol needs to be a '.' symbol. var typeName = TypeNameHelper.GetTypeDisplayName(type, fullName: true, nestedTypeDelimiter: '.'); var name = $"System.IParsable<{typeName}>.TryParse"; methodInfo = type.GetMethod(name, BindingFlags.Static | BindingFlags.NonPublic)!; return methodInfo is not null; } [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] private MethodInfo? GetStaticMethodFromHierarchy(Type type, string name, Type[] parameterTypes, Func<MethodInfo, bool> validateReturnType) { bool IsMatch(MethodInfo? method) => method is not null && !method.IsAbstract && validateReturnType(method); var methodInfo = type.GetMethod(name, BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy, parameterTypes); if (IsMatch(methodInfo)) { return methodInfo; } var candidateInterfaceMethodInfo = default(MethodInfo); // Check all interfaces for implementations. Fail if there are duplicates. foreach (var implementedInterface in type.GetInterfaces()) { var interfaceMethod = implementedInterface.GetMethod(name, BindingFlags.Public | BindingFlags.Static, parameterTypes); if (IsMatch(interfaceMethod)) { if (candidateInterfaceMethodInfo is not null) { if (_throwOnInvalidMethod) { throw new InvalidOperationException($"{TypeNameHelper.GetTypeDisplayName(type, fullName: false)} implements multiple interfaces defining a static {interfaceMethod} method causing ambiguity."); } return null; } candidateInterfaceMethodInfo = interfaceMethod; } } return candidateInterfaceMethodInfo; } [RequiresUnreferencedCode("Performs reflection on type hierarchy. This cannot be statically analyzed.")] private static MethodInfo? GetAnyMethodFromHierarchy(Type type, string name) { // Find first incorrectly formatted method var methodInfo = type.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy) .FirstOrDefault(methodInfo => methodInfo.Name == name); if (methodInfo is not null) { return methodInfo; } foreach (var implementedInterface in type.GetInterfaces()) { var interfaceMethod = implementedInterface.GetMethod(name, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); if (interfaceMethod is not null) { return interfaceMethod; } } return null; } private static MethodInfo GetEnumTryParseMethod(bool preferNonGenericEnumParseOverload) { MethodInfo? methodInfo = null; if (preferNonGenericEnumParseOverload) { methodInfo = typeof(Enum).GetMethod( nameof(Enum.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(Type), typeof(string), typeof(object).MakeByRefType() }); } else { methodInfo = typeof(Enum).GetMethod( nameof(Enum.TryParse), genericParameterCount: 1, new[] { typeof(string), Type.MakeGenericMethodParameter(0).MakeByRefType() }); } if (methodInfo is null) { Debug.Fail("No suitable System.Enum.TryParse method found."); throw new MissingMethodException("No suitable System.Enum.TryParse method found."); } return methodInfo!; } private static bool TryGetDateTimeTryParseMethod(Type type, [NotNullWhen(true)] out MethodInfo? methodInfo) { methodInfo = null; if (type == typeof(DateTime)) { methodInfo = typeof(DateTime).GetMethod( nameof(DateTime.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(IFormatProvider), typeof(DateTimeStyles), typeof(DateTime).MakeByRefType() }); } else if (type == typeof(DateTimeOffset)) { methodInfo = typeof(DateTimeOffset).GetMethod( nameof(DateTimeOffset.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(IFormatProvider), typeof(DateTimeStyles), typeof(DateTimeOffset).MakeByRefType() }); } else if (type == typeof(DateOnly)) { methodInfo = typeof(DateOnly).GetMethod( nameof(DateOnly.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(IFormatProvider), typeof(DateTimeStyles), typeof(DateOnly).MakeByRefType() }); } else if (type == typeof(TimeOnly)) { methodInfo = typeof(TimeOnly).GetMethod( nameof(TimeOnly.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(IFormatProvider), typeof(DateTimeStyles), typeof(TimeOnly).MakeByRefType() }); } return methodInfo != null; } private static bool TryGetNumberStylesTryGetMethod(Type type, [NotNullWhen(true)] out MethodInfo? method, [NotNullWhen(true)] out NumberStyles? numberStyles) { method = null; numberStyles = NumberStyles.Integer; if (type == typeof(long)) { method = typeof(long).GetMethod( nameof(long.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(long).MakeByRefType() }); } else if (type == typeof(ulong)) { method = typeof(ulong).GetMethod( nameof(ulong.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(ulong).MakeByRefType() }); } else if (type == typeof(int)) { method = typeof(int).GetMethod( nameof(int.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(int).MakeByRefType() }); } else if (type == typeof(uint)) { method = typeof(uint).GetMethod( nameof(uint.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(uint).MakeByRefType() }); } else if (type == typeof(short)) { method = typeof(short).GetMethod( nameof(short.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(short).MakeByRefType() }); } else if (type == typeof(ushort)) { method = typeof(ushort).GetMethod( nameof(ushort.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(ushort).MakeByRefType() }); } else if (type == typeof(byte)) { method = typeof(byte).GetMethod( nameof(byte.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(byte).MakeByRefType() }); } else if (type == typeof(sbyte)) { method = typeof(sbyte).GetMethod( nameof(sbyte.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(sbyte).MakeByRefType() }); } else if (type == typeof(double)) { method = typeof(double).GetMethod( nameof(double.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(double).MakeByRefType() }); numberStyles = NumberStyles.AllowThousands | NumberStyles.Float; } else if (type == typeof(float)) { method = typeof(float).GetMethod( nameof(float.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(float).MakeByRefType() }); numberStyles = NumberStyles.AllowThousands | NumberStyles.Float; } else if (type == typeof(Half)) { method = typeof(Half).GetMethod( nameof(Half.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(Half).MakeByRefType() }); numberStyles = NumberStyles.AllowThousands | NumberStyles.Float; } else if (type == typeof(decimal)) { method = typeof(decimal).GetMethod( nameof(decimal.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(decimal).MakeByRefType() }); numberStyles = NumberStyles.Number; } else if (type == typeof(IntPtr)) { method = typeof(IntPtr).GetMethod( nameof(IntPtr.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(IntPtr).MakeByRefType() }); } else if (type == typeof(BigInteger)) { method = typeof(BigInteger).GetMethod( nameof(BigInteger.TryParse), BindingFlags.Public | BindingFlags.Static, new[] { typeof(string), typeof(NumberStyles), typeof(IFormatProvider), typeof(BigInteger).MakeByRefType() }); } return method != null; } private static ValueTask<object?> ConvertValueTask<T>(ValueTask<T> typedValueTask) { if (typedValueTask.IsCompletedSuccessfully) { var result = typedValueTask.GetAwaiter().GetResult(); return new ValueTask<object?>(result); } static async ValueTask<object?> ConvertAwaited(ValueTask<T> typedValueTask) => await typedValueTask; return ConvertAwaited(typedValueTask); } private static ValueTask<object?> ConvertValueTaskOfNullableResult<T>(ValueTask<Nullable<T>> typedValueTask) where T : struct { if (typedValueTask.IsCompletedSuccessfully) { var result = typedValueTask.GetAwaiter().GetResult(); return new ValueTask<object?>(result); } static async ValueTask<object?> ConvertAwaited(ValueTask<Nullable<T>> typedValueTask) => await typedValueTask; return ConvertAwaited(typedValueTask); } private sealed class ParameterLookupKey { public ParameterLookupKey(string name, Type type) { Name = name; Type = type; } public string Name { get; } public Type Type { get; } public override int GetHashCode() { return StringComparer.OrdinalIgnoreCase.GetHashCode(Name); } public override bool Equals([NotNullWhen(true)] object? obj) { Debug.Assert(obj is ParameterLookupKey); var other = (ParameterLookupKey)obj; return Type == other.Type && string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase); } } internal sealed class ConstructorParameter { public ConstructorParameter(ParameterInfo parameter, PropertyInfo propertyInfo) { ParameterInfo = parameter; PropertyInfo = propertyInfo; } public ParameterInfo ParameterInfo { get; } public PropertyInfo PropertyInfo { get; } } }