|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace System.Dynamic.Utils
{
internal static class TypeUtils
{
private static readonly Type[] s_arrayAssignableInterfaces = typeof(int[]).GetInterfaces()
.Where(i => i.IsGenericType)
.Select(i => i.GetGenericTypeDefinition())
.ToArray();
private static readonly ConstructorInfo s_nullableConstructor = typeof(Nullable<>).GetConstructor(typeof(Nullable<>).GetGenericArguments())!;
public static Type GetNonNullableType(this Type type) => IsNullableType(type) ? type.GetGenericArguments()[0] : type;
[RequiresDynamicCode("Creating nullable types requires dynamic code.")]
public static Type GetNullableType(this Type type)
{
Debug.Assert(type != null, "type cannot be null");
if (type.IsValueType && !IsNullableType(type))
{
return typeof(Nullable<>).MakeGenericType(type);
}
return type;
}
/// <summary>
/// This is an alternative to <see cref="GetNullableType" /> that will throw
/// if dynamic code is required. Some common primitive types are special-cased.
/// </summary>
public static Type LiftPrimitiveOrThrow(this Type type)
{
if (RuntimeFeature.IsDynamicCodeSupported)
{
return GetNullableType(type);
}
if (!type.IsValueType || IsNullableType(type))
{
return type;
}
switch (type.GetTypeCode())
{
case TypeCode.Boolean:
return typeof(bool?);
case TypeCode.Int32:
return typeof(int?);
case TypeCode.Int64:
return typeof(long?);
case TypeCode.Single:
return typeof(float?);
case TypeCode.Double:
return typeof(double?);
case TypeCode.UInt32:
return typeof(uint?);
case TypeCode.UInt64:
return typeof(ulong?);
case TypeCode.Byte:
return typeof(byte?);
case TypeCode.SByte:
return typeof(sbyte?);
case TypeCode.Int16:
return typeof(short?);
case TypeCode.UInt16:
return typeof(ushort?);
case TypeCode.Char:
return typeof(char?);
case TypeCode.Decimal:
return typeof(decimal?);
case TypeCode.DateTime:
return typeof(DateTime?);
default:
if (type == typeof(DateTimeOffset))
{
return typeof(DateTimeOffset?);
}
else if (type == typeof(TimeSpan))
{
return typeof(TimeSpan?);
}
throw new NotSupportedException(Strings.LiftingInExpressionRequiresDynamicCode(type));
}
}
public static ConstructorInfo GetNullableConstructor(Type nullableType)
{
Debug.Assert(nullableType.IsNullableType());
return (ConstructorInfo)nullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableConstructor);
}
public static bool IsNullableType(this Type type) => type.IsConstructedGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
public static bool IsNullableOrReferenceType(this Type type) => !type.IsValueType || IsNullableType(type);
public static bool IsBool(this Type type) => GetNonNullableType(type) == typeof(bool);
public static bool IsNumeric(this Type type)
{
type = GetNonNullableType(type);
if (!type.IsEnum)
{
switch (type.GetTypeCode())
{
case TypeCode.Char:
case TypeCode.SByte:
case TypeCode.Byte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Double:
case TypeCode.Single:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return true;
}
}
return false;
}
public static bool IsInteger(this Type type)
{
type = GetNonNullableType(type);
if (!type.IsEnum)
{
switch (type.GetTypeCode())
{
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return true;
}
}
return false;
}
public static bool IsInteger64(this Type type)
{
type = GetNonNullableType(type);
if (!type.IsEnum)
{
switch (type.GetTypeCode())
{
case TypeCode.Int64:
case TypeCode.UInt64:
return true;
}
}
return false;
}
public static bool IsArithmetic(this Type type)
{
type = GetNonNullableType(type);
if (!type.IsEnum)
{
switch (type.GetTypeCode())
{
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Double:
case TypeCode.Single:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return true;
}
}
return false;
}
public static bool IsUnsignedInt(this Type type)
{
type = GetNonNullableType(type);
if (!type.IsEnum)
{
switch (type.GetTypeCode())
{
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
return true;
}
}
return false;
}
public static bool IsIntegerOrBool(this Type type)
{
type = GetNonNullableType(type);
if (!type.IsEnum)
{
switch (type.GetTypeCode())
{
case TypeCode.Int64:
case TypeCode.Int32:
case TypeCode.Int16:
case TypeCode.UInt64:
case TypeCode.UInt32:
case TypeCode.UInt16:
case TypeCode.Boolean:
case TypeCode.SByte:
case TypeCode.Byte:
return true;
}
}
return false;
}
public static bool IsNumericOrBool(this Type type) => IsNumeric(type) || IsBool(type);
// Checks if the type is a valid target for an instance call
public static bool IsValidInstanceType(MemberInfo member, Type instanceType)
{
Type? targetType = member.DeclaringType;
if (targetType == null)
{
return false;
}
if (AreReferenceAssignable(targetType, instanceType))
{
return true;
}
if (instanceType.IsValueType)
{
if (AreReferenceAssignable(targetType, typeof(object)))
{
return true;
}
if (AreReferenceAssignable(targetType, typeof(ValueType)))
{
return true;
}
if (instanceType.IsEnum && AreReferenceAssignable(targetType, typeof(Enum)))
{
return true;
}
// A call to an interface implemented by a struct is legal whether the struct has
// been boxed or not.
if (targetType.IsInterface)
{
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The targetType must be preserved (since we have an instance of it here)," +
"So if it's an interface that interface will be preserved everywhere" +
"So if it was implemented by the instanceType, it will be kept even after trimming." +
"The fact that GetInterfaces may return fewer interfaces doesn't matter as long" +
"as it returns the one we're looking for.")]
static Type[] GetTypeInterfaces(Type instanceType) => instanceType.GetInterfaces();
foreach (Type interfaceType in GetTypeInterfaces(instanceType))
{
if (AreReferenceAssignable(targetType, interfaceType))
{
return true;
}
}
}
}
return false;
}
public static bool HasIdentityPrimitiveOrNullableConversionTo(this Type source, Type dest)
{
Debug.Assert(source != null && dest != null);
// Identity conversion
if (AreEquivalent(source, dest))
{
return true;
}
// Nullable conversions
if (IsNullableType(source) && AreEquivalent(dest, GetNonNullableType(source)))
{
return true;
}
if (IsNullableType(dest) && AreEquivalent(source, GetNonNullableType(dest)))
{
return true;
}
// Primitive runtime conversions
// All conversions amongst enum, bool, char, integer and float types
// (and their corresponding nullable types) are legal except for
// nonbool==>bool and nonbool==>bool? which are only legal from
// bool-backed enums.
return IsConvertible(source) && IsConvertible(dest)
&& (GetNonNullableType(dest) != typeof(bool)
|| source.IsEnum && source.GetEnumUnderlyingType() == typeof(bool));
}
public static bool HasReferenceConversionTo(this Type source, Type dest)
{
Debug.Assert(source != null && dest != null);
// void -> void conversion is handled elsewhere
// (it's an identity conversion)
// All other void conversions are disallowed.
if (source == typeof(void) || dest == typeof(void))
{
return false;
}
Type nnSourceType = GetNonNullableType(source);
Type nnDestType = GetNonNullableType(dest);
// Down conversion
if (nnSourceType.IsAssignableFrom(nnDestType))
{
return true;
}
// Up conversion
if (nnDestType.IsAssignableFrom(nnSourceType))
{
return true;
}
// Interface conversion
if (source.IsInterface || dest.IsInterface)
{
return true;
}
// Variant delegate conversion
if (IsLegalExplicitVariantDelegateConversion(source, dest))
{
return true;
}
// Object conversion handled by assignable above.
Debug.Assert(source != typeof(object) && dest != typeof(object));
return (source.IsArray || dest.IsArray) && StrictHasReferenceConversionTo(source, dest, true);
}
private static bool StrictHasReferenceConversionTo(this Type source, Type dest, bool skipNonArray)
{
// HasReferenceConversionTo was both too strict and too lax. It was too strict in prohibiting
// some valid conversions involving arrays, and too lax in allowing casts between interfaces
// and sealed classes that don't implement them. Unfortunately fixing the lax cases would be
// a breaking change, especially since such expressions will even work if only given null
// arguments.
// This method catches the cases that were incorrectly disallowed, but when it needs to
// examine possible conversions of element or type parameters it applies stricter rules.
while (true)
{
if (!skipNonArray) // Skip if we just came from HasReferenceConversionTo and have just tested these
{
if (source.IsValueType | dest.IsValueType)
{
return false;
}
// Includes to case of either being typeof(object)
if (source.IsAssignableFrom(dest) || dest.IsAssignableFrom(source))
{
return true;
}
if (source.IsInterface)
{
if (dest.IsInterface || dest.IsClass && !dest.IsSealed)
{
return true;
}
}
else if (dest.IsInterface)
{
if (source.IsClass && !source.IsSealed)
{
return true;
}
}
}
if (source.IsArray)
{
if (dest.IsArray)
{
if (source.GetArrayRank() != dest.GetArrayRank() || source.IsSZArray != dest.IsSZArray)
{
return false;
}
source = source.GetElementType()!;
dest = dest.GetElementType()!;
skipNonArray = false;
}
else
{
return HasArrayToInterfaceConversion(source, dest);
}
}
else if (dest.IsArray)
{
if (HasInterfaceToArrayConversion(source, dest))
{
return true;
}
return IsImplicitReferenceConversion(typeof(Array), source);
}
else
{
return IsLegalExplicitVariantDelegateConversion(source, dest);
}
}
}
private static bool HasArrayToInterfaceConversion(Type source, Type dest)
{
Debug.Assert(source.IsArray);
if (!source.IsSZArray || !dest.IsInterface || !dest.IsGenericType)
{
return false;
}
Type[] destParams = dest.GetGenericArguments();
if (destParams.Length != 1)
{
return false;
}
Type destGen = dest.GetGenericTypeDefinition();
foreach (Type iface in s_arrayAssignableInterfaces)
{
if (AreEquivalent(destGen, iface))
{
return StrictHasReferenceConversionTo(source.GetElementType()!, destParams[0], false);
}
}
return false;
}
private static bool HasInterfaceToArrayConversion(Type source, Type dest)
{
Debug.Assert(dest.IsSZArray);
if (!dest.IsSZArray || !source.IsInterface || !source.IsGenericType)
{
return false;
}
Type[] sourceParams = source.GetGenericArguments();
if (sourceParams.Length != 1)
{
return false;
}
Type sourceGen = source.GetGenericTypeDefinition();
foreach (Type iface in s_arrayAssignableInterfaces)
{
if (AreEquivalent(sourceGen, iface))
{
return StrictHasReferenceConversionTo(sourceParams[0], dest.GetElementType()!, false);
}
}
return false;
}
private static bool IsCovariant(Type t)
{
Debug.Assert(t != null);
return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Covariant);
}
private static bool IsContravariant(Type t)
{
Debug.Assert(t != null);
return 0 != (t.GenericParameterAttributes & GenericParameterAttributes.Contravariant);
}
private static bool IsInvariant(Type t)
{
Debug.Assert(t != null);
return 0 == (t.GenericParameterAttributes & GenericParameterAttributes.VarianceMask);
}
private static bool IsDelegate(Type t)
{
Debug.Assert(t != null);
return t.IsSubclassOf(typeof(MulticastDelegate));
}
public static bool IsLegalExplicitVariantDelegateConversion(Type source, Type dest)
{
Debug.Assert(source != null && dest != null);
// There *might* be a legal conversion from a generic delegate type S to generic delegate type T,
// provided all of the follow are true:
// o Both types are constructed generic types of the same generic delegate type, D<X1,... Xk>.
// That is, S = D<S1...>, T = D<T1...>.
// o If type parameter Xi is declared to be invariant then Si must be identical to Ti.
// o If type parameter Xi is declared to be covariant ("out") then Si must be convertible
// to Ti via an identify conversion, implicit reference conversion, or explicit reference conversion.
// o If type parameter Xi is declared to be contravariant ("in") then either Si must be identical to Ti,
// or Si and Ti must both be reference types.
if (!IsDelegate(source) || !IsDelegate(dest) || !source.IsGenericType || !dest.IsGenericType)
{
return false;
}
Type genericDelegate = source.GetGenericTypeDefinition();
if (dest.GetGenericTypeDefinition() != genericDelegate)
{
return false;
}
Type[] genericParameters = genericDelegate.GetGenericArguments();
Type[] sourceArguments = source.GetGenericArguments();
Type[] destArguments = dest.GetGenericArguments();
Debug.Assert(genericParameters != null);
Debug.Assert(sourceArguments != null);
Debug.Assert(destArguments != null);
Debug.Assert(genericParameters.Length == sourceArguments.Length);
Debug.Assert(genericParameters.Length == destArguments.Length);
for (int iParam = 0; iParam < genericParameters.Length; ++iParam)
{
Type sourceArgument = sourceArguments[iParam];
Type destArgument = destArguments[iParam];
Debug.Assert(sourceArgument != null && destArgument != null);
// If the arguments are identical then this one is automatically good, so skip it.
if (AreEquivalent(sourceArgument, destArgument))
{
continue;
}
Type genericParameter = genericParameters[iParam];
Debug.Assert(genericParameter != null);
if (IsInvariant(genericParameter))
{
return false;
}
if (IsCovariant(genericParameter))
{
if (!sourceArgument.HasReferenceConversionTo(destArgument))
{
return false;
}
}
else if (IsContravariant(genericParameter) && (sourceArgument.IsValueType || destArgument.IsValueType))
{
return false;
}
}
return true;
}
public static bool IsConvertible(this Type type)
{
type = GetNonNullableType(type);
if (type.IsEnum)
{
return true;
}
switch (type.GetTypeCode())
{
case TypeCode.Boolean:
case TypeCode.Byte:
case TypeCode.SByte:
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.UInt16:
case TypeCode.UInt32:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Char:
return true;
default:
return false;
}
}
public static bool HasReferenceEquality(Type left, Type right)
{
if (left.IsValueType || right.IsValueType)
{
return false;
}
// If we have an interface and a reference type then we can do
// reference equality.
// If we have two reference types and one is assignable to the
// other then we can do reference equality.
return left.IsInterface || right.IsInterface || AreReferenceAssignable(left, right)
|| AreReferenceAssignable(right, left);
}
public static bool HasBuiltInEqualityOperator(Type left, Type right)
{
// If we have an interface and a reference type then we can do
// reference equality.
if (left.IsInterface && !right.IsValueType)
{
return true;
}
if (right.IsInterface && !left.IsValueType)
{
return true;
}
// If we have two reference types and one is assignable to the
// other then we can do reference equality.
if (!left.IsValueType && !right.IsValueType)
{
if (AreReferenceAssignable(left, right) || AreReferenceAssignable(right, left))
{
return true;
}
}
// Otherwise, if the types are not the same then we definitely
// do not have a built-in equality operator.
if (!AreEquivalent(left, right))
{
return false;
}
// We have two identical value types, modulo nullability. (If they were both the
// same reference type then we would have returned true earlier.)
Debug.Assert(left.IsValueType);
// Equality between struct types is only defined for numerics, bools, enums,
// and their nullable equivalents.
Type nnType = GetNonNullableType(left);
return nnType == typeof(bool) || IsNumeric(nnType) || nnType.IsEnum;
}
public static bool IsImplicitlyConvertibleTo(this Type source, Type destination) =>
AreEquivalent(source, destination) // identity conversion
|| IsImplicitNumericConversion(source, destination)
|| IsImplicitReferenceConversion(source, destination)
|| IsImplicitBoxingConversion(source, destination)
|| IsImplicitNullableConversion(source, destination);
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
Justification = "The trimmer doesn't remove operators when System.Linq.Expressions is used. See https://github.com/mono/linker/pull/2125.")]
public static MethodInfo? GetUserDefinedCoercionMethod(Type convertFrom, Type convertToType)
{
Type nnExprType = GetNonNullableType(convertFrom);
Type nnConvType = GetNonNullableType(convertToType);
// try exact match on types
MethodInfo[] eMethods = nnExprType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
MethodInfo? method = FindConversionOperator(eMethods, convertFrom, convertToType);
if (method != null)
{
return method;
}
MethodInfo[] cMethods = nnConvType.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
method = FindConversionOperator(cMethods, convertFrom, convertToType);
if (method != null)
{
return method;
}
if (AreEquivalent(nnExprType, convertFrom) && AreEquivalent(nnConvType, convertToType))
{
return null;
}
// try lifted conversion
return FindConversionOperator(eMethods, nnExprType, nnConvType)
?? FindConversionOperator(cMethods, nnExprType, nnConvType)
?? FindConversionOperator(eMethods, nnExprType, convertToType)
?? FindConversionOperator(cMethods, nnExprType, convertToType);
}
private static MethodInfo? FindConversionOperator(MethodInfo[] methods, Type? typeFrom, Type? typeTo)
{
foreach (MethodInfo mi in methods)
{
if ((mi.Name == "op_Implicit" || mi.Name == "op_Explicit") && AreEquivalent(mi.ReturnType, typeTo))
{
ParameterInfo[] pis = mi.GetParametersCached();
if (pis.Length == 1 && AreEquivalent(pis[0].ParameterType, typeFrom))
{
return mi;
}
}
}
return null;
}
private static bool IsImplicitNumericConversion(Type source, Type destination)
{
TypeCode tcSource = source.GetTypeCode();
TypeCode tcDest = destination.GetTypeCode();
switch (tcSource)
{
case TypeCode.SByte:
switch (tcDest)
{
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Byte:
switch (tcDest)
{
case TypeCode.Int16:
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Int16:
switch (tcDest)
{
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.UInt16:
switch (tcDest)
{
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Int32:
switch (tcDest)
{
case TypeCode.Int64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.UInt32:
switch (tcDest)
{
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Int64:
case TypeCode.UInt64:
switch (tcDest)
{
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Char:
switch (tcDest)
{
case TypeCode.UInt16:
case TypeCode.Int32:
case TypeCode.UInt32:
case TypeCode.Int64:
case TypeCode.UInt64:
case TypeCode.Single:
case TypeCode.Double:
case TypeCode.Decimal:
return true;
}
break;
case TypeCode.Single:
return tcDest == TypeCode.Double;
}
return false;
}
private static bool IsImplicitReferenceConversion(Type source, Type destination) =>
destination.IsAssignableFrom(source);
private static bool IsImplicitBoxingConversion(Type source, Type destination) =>
source.IsValueType && (destination == typeof(object) || destination == typeof(ValueType)) || source.IsEnum && destination == typeof(Enum);
private static bool IsImplicitNullableConversion(Type source, Type destination) =>
IsNullableType(destination) && IsImplicitlyConvertibleTo(GetNonNullableType(source), GetNonNullableType(destination));
public static Type? FindGenericType(Type definition, Type? type)
{
// For now this helper doesn't support interfaces
Debug.Assert(!definition.IsInterface);
while (type is not null && type != typeof(object))
{
if (type.IsConstructedGenericType && AreEquivalent(type.GetGenericTypeDefinition(), definition))
{
return type;
}
type = type.BaseType;
}
return null;
}
/// <summary>
/// Searches for an operator method on the type. The method must have
/// the specified signature, no generic arguments, and have the
/// SpecialName bit set. Also searches inherited operator methods.
///
/// NOTE: This was designed to satisfy the needs of op_True and
/// op_False, because we have to do runtime lookup for those. It may
/// not work right for unary operators in general.
/// </summary>
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2067:UnrecognizedReflectionPattern",
Justification = "The trimmer doesn't remove operators when System.Linq.Expressions is used. See https://github.com/mono/linker/pull/2125.")]
public static MethodInfo? GetBooleanOperator(Type type, string name)
{
Debug.Assert(name == "op_False" || name == "op_True");
do
{
MethodInfo? result = type.GetAnyStaticMethodValidated(name, new[] { type });
if (result != null && result.IsSpecialName && !result.ContainsGenericParameters)
{
return result;
}
type = type.BaseType!;
} while (type != null);
return null;
}
public static Type GetNonRefType(this Type type) => type.IsByRef ? type.GetElementType()! : type;
public static bool AreEquivalent(Type? t1, Type? t2) => t1 != null && t1.IsEquivalentTo(t2);
public static bool AreReferenceAssignable(Type dest, Type src)
{
// This actually implements "Is this identity assignable and/or reference assignable?"
if (AreEquivalent(dest, src))
{
return true;
}
return !dest.IsValueType && !src.IsValueType && dest.IsAssignableFrom(src);
}
public static bool IsSameOrSubclass(Type type, Type subType) =>
AreEquivalent(type, subType) || subType.IsSubclassOf(type);
public static void ValidateType(Type type, string? paramName) => ValidateType(type, paramName, false, false);
public static void ValidateType(Type type, string? paramName, bool allowByRef, bool allowPointer)
{
if (ValidateType(type, paramName, -1))
{
if (!allowByRef && type.IsByRef)
{
throw Error.TypeMustNotBeByRef(paramName);
}
if (!allowPointer && type.IsPointer)
{
throw Error.TypeMustNotBePointer(paramName);
}
}
}
public static bool ValidateType(Type type, string? paramName, int index)
{
if (type == typeof(void))
{
return false; // Caller can skip further checks.
}
if (type.ContainsGenericParameters)
{
throw type.IsGenericTypeDefinition
? Error.TypeIsGeneric(type, paramName, index)
: Error.TypeContainsGenericParameters(type, paramName, index);
}
return true;
}
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The trimmer will never remove the Invoke method from delegates.")]
public static MethodInfo GetInvokeMethod(this Type delegateType)
{
Debug.Assert(typeof(Delegate).IsAssignableFrom(delegateType));
return delegateType.GetMethod("Invoke", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)!;
}
internal static bool IsUnsigned(this Type type) => IsUnsigned(GetNonNullableType(type).GetTypeCode());
internal static bool IsUnsigned(this TypeCode typeCode)
{
switch (typeCode)
{
case TypeCode.Byte:
case TypeCode.UInt16:
case TypeCode.Char:
case TypeCode.UInt32:
case TypeCode.UInt64:
return true;
default:
return false;
}
}
internal static bool IsFloatingPoint(this Type type) => IsFloatingPoint(GetNonNullableType(type).GetTypeCode());
internal static bool IsFloatingPoint(this TypeCode typeCode)
{
switch (typeCode)
{
case TypeCode.Single:
case TypeCode.Double:
return true;
default:
return false;
}
}
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The Array 'Get' method is dynamically constructed and is not included in IL. It is not subject to trimming.")]
public static MethodInfo GetArrayGetMethod(Type arrayType)
{
Debug.Assert(arrayType.IsArray);
return arrayType.GetMethod("Get", BindingFlags.Public | BindingFlags.Instance)!;
}
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The Array 'Set' method is dynamically constructed and is not included in IL. It is not subject to trimming.")]
public static MethodInfo GetArraySetMethod(Type arrayType)
{
Debug.Assert(arrayType.IsArray);
return arrayType.GetMethod("Set", BindingFlags.Public | BindingFlags.Instance)!;
}
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2070:UnrecognizedReflectionPattern",
Justification = "The Array 'Address' method is dynamically constructed and is not included in IL. It is not subject to trimming.")]
public static MethodInfo GetArrayAddressMethod(Type arrayType)
{
Debug.Assert(arrayType.IsArray);
return arrayType.GetMethod("Address", BindingFlags.Public | BindingFlags.Instance)!;
}
}
}
|