Reflect
Helper methods for inspecting a type by reflection.
Many of these methods take ICustomAttributeProvider as an
argument to avoid duplication, even though certain attributes can
only appear on specific types of members, like MethodInfo or Type.
In the case where a type is being examined for the presence of
an attribute, interface or named member, the Reflect methods
operate with the full name of the member being sought. This
removes the necessity of the caller having a reference to the
assembly that defines the item being sought and allows the
NUnit core to inspect assemblies that reference an older
version of the NUnit framework.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace NUnit.Framework.Internal
{
[System.Runtime.CompilerServices.NullableContext(1)]
[System.Runtime.CompilerServices.Nullable(0)]
public static class Reflect
{
internal static readonly BindingFlags AllMembers = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;
private static readonly Dictionary<Type, List<Type>> ConvertibleValueTypes = new Dictionary<Type, List<Type>> {
{
typeof(decimal),
new List<Type> {
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(char)
}
},
{
typeof(double),
new List<Type> {
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(char),
typeof(float)
}
},
{
typeof(float),
new List<Type> {
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong),
typeof(char),
typeof(float)
}
},
{
typeof(ulong),
new List<Type> {
typeof(byte),
typeof(ushort),
typeof(uint),
typeof(char)
}
},
{
typeof(long),
new List<Type> {
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(char)
}
},
{
typeof(uint),
new List<Type> {
typeof(byte),
typeof(ushort),
typeof(char)
}
},
{
typeof(int),
new List<Type> {
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(char)
}
},
{
typeof(ushort),
new List<Type> {
typeof(byte),
typeof(char)
}
},
{
typeof(short),
new List<Type> {
typeof(byte)
}
}
};
public static bool HasMethodWithAttribute(Type fixtureType, Type attributeType)
{
MethodInfo[] methods = fixtureType.GetMethods(AllMembers | BindingFlags.FlattenHierarchy);
foreach (MethodInfo methodInfo in methods) {
if (methodInfo.IsDefined(attributeType, false))
return true;
}
return false;
}
public static object Construct(Type type)
{
ConstructorInfo constructor = type.GetConstructor(Array.Empty<Type>());
if ((object)constructor == null)
throw new InvalidTestFixtureException(type.FullName + " does not have a default constructor");
return constructor.Invoke(null);
}
public static object Construct(Type type, [System.Runtime.CompilerServices.Nullable(2)] object[] arguments)
{
if (arguments == null)
return Construct(type);
Type[] typeArray = GetTypeArray(arguments);
ConstructorInfo constructorInfo = GetConstructors(type, typeArray).FirstOrDefault();
if ((object)constructorInfo == null)
throw new InvalidTestFixtureException(type.FullName + " does not have a suitable constructor");
ParameterInfo[] parameters = constructorInfo.GetParameters();
if (parameters.Length != 0) {
ParameterInfo parameterInfo = parameters.Last();
if (parameterInfo.HasAttribute<ParamArrayAttribute>(false) && (arguments.Length != parameters.Length || !parameterInfo.ParameterType.IsAssignableFrom(arguments[parameters.Length - 1]?.GetType()))) {
Type elementType = parameterInfo.ParameterType.GetElementType();
if ((object)elementType == null)
throw new InvalidTestFixtureException(type.FullName + " params argument did not have an element type");
int num = parameters.Length - 1;
Array array = Array.CreateInstance(elementType, typeArray.Length - parameters.Length + 1);
for (int i = 0; i < array.Length; i++) {
array.SetValue(arguments[i + num], i);
}
arguments = arguments.Take(parameters.Length - 1).Concat(new object[1] {
array
}).ToArray();
}
}
return constructorInfo.Invoke(arguments);
}
[return: System.Runtime.CompilerServices.Nullable(new byte[] {
1,
2
})]
internal static Type[] GetTypeArray([System.Runtime.CompilerServices.Nullable(new byte[] {
1,
2
})] object[] objects)
{
Type[] array = new Type[objects.Length];
int num = 0;
foreach (object obj in objects) {
array[num++] = obj?.GetType();
}
return array;
}
internal static IEnumerable<ConstructorInfo> GetConstructors(Type type, [System.Runtime.CompilerServices.Nullable(new byte[] {
1,
2
})] Type[] matchingTypes)
{
return from c in type.GetConstructors()
where c.GetParameters().ParametersMatch(matchingTypes)
select c;
}
internal static bool ParametersMatch(this ParameterInfo[] pinfos, [System.Runtime.CompilerServices.Nullable(new byte[] {
1,
2
})] Type[] ptypes)
{
bool flag = pinfos.Length != 0 && pinfos[pinfos.Length - 1].HasAttribute<ParamArrayAttribute>(false);
if (flag) {
if (ptypes.Length < pinfos.Length - 1)
return false;
} else if (pinfos.Length != ptypes.Length) {
return false;
}
for (int i = 0; i < pinfos.Length; i++) {
if (flag && i == pinfos.Length - 1) {
Type elementType = pinfos[i].ParameterType.GetElementType();
if ((object)elementType != null) {
bool flag2 = true;
for (int j = i; (j < ptypes.Length) & flag2; j++) {
if (!ptypes[j].CanImplicitlyConvertTo(elementType)) {
flag2 = false;
break;
}
}
if (flag2)
continue;
}
}
if (!ptypes[i].CanImplicitlyConvertTo(pinfos[i].ParameterType))
return false;
}
return true;
}
internal static bool CanImplicitlyConvertTo([System.Runtime.CompilerServices.Nullable(2)] this Type from, Type to)
{
if (to.IsAssignableFrom(from))
return true;
if ((object)from == null) {
if (!to.IsClass)
return to.FullName().StartsWith("System.Nullable", StringComparison.Ordinal);
return true;
}
if (ConvertibleValueTypes.TryGetValue(to, out List<Type> value) && value.Contains(from))
return true;
return from.GetMethods(BindingFlags.Static | BindingFlags.Public).Any(delegate(MethodInfo m) {
if (m.ReturnType == to)
return m.Name == "op_Implicit";
return false;
});
}
[System.Runtime.CompilerServices.NullableContext(2)]
public static object InvokeMethod([System.Runtime.CompilerServices.Nullable(1)] MethodInfo method, object fixture)
{
return InvokeMethod(method, fixture, null);
}
[System.Runtime.CompilerServices.NullableContext(2)]
public static object InvokeMethod([System.Runtime.CompilerServices.Nullable(1)] MethodInfo method, object fixture, params object[] args)
{
try {
return method.Invoke(fixture, args);
} catch (TargetInvocationException exception) {
throw new NUnitException("Rethrown", exception.Unwrap());
} catch (Exception inner) {
throw new NUnitException("Rethrown", inner);
}
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public static PropertyInfo GetUltimateShadowingProperty(Type type, string name, BindingFlags bindingFlags)
{
Guard.ArgumentNotNull(type, "type");
Guard.ArgumentNotNull(name, "name");
if ((bindingFlags & BindingFlags.DeclaredOnly) != 0)
return type.GetProperty(name, bindingFlags);
if ((bindingFlags & (BindingFlags.Public | BindingFlags.NonPublic)) == (BindingFlags.Public | BindingFlags.NonPublic)) {
Type type2 = type;
while ((object)type2 != null) {
PropertyInfo property = type2.GetProperty(name, (bindingFlags | BindingFlags.DeclaredOnly) & ~BindingFlags.NonPublic);
if ((object)property != null)
return property;
type2 = type2.BaseType;
}
bindingFlags &= ~BindingFlags.Public;
}
Type type3 = type;
while ((object)type3 != null) {
PropertyInfo property2 = type3.GetProperty(name, bindingFlags | BindingFlags.DeclaredOnly);
if ((object)property2 != null)
return property2;
type3 = type3.BaseType;
}
return null;
}
internal static bool IsAssignableFromNull(Type type)
{
Guard.ArgumentNotNull(type, "type");
if (type.IsValueType)
return IsNullable(type);
return true;
}
private static bool IsNullable(Type type)
{
if (type.IsGenericType && !type.IsGenericTypeDefinition)
return (object)type.GetGenericTypeDefinition() == typeof(Nullable<>);
return false;
}
internal static IEnumerable<Type> TypeAndBaseTypes([System.Runtime.CompilerServices.Nullable(2)] this Type type)
{
while ((object)type != null) {
yield return type;
type = type.BaseType;
}
}
[return: System.Runtime.CompilerServices.Nullable(2)]
internal static MethodInfo GetNonGenericPublicInstanceMethod(this Type type, string name, Type[] parameterTypes)
{
foreach (Type item in type.TypeAndBaseTypes()) {
MethodInfo methodInfo = item.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public).SingleOrDefault(delegate(MethodInfo candidate) {
if (candidate.Name != name || candidate.GetGenericArguments().Length != 0)
return false;
ParameterInfo[] parameters = candidate.GetParameters();
if (parameters.Length != parameterTypes.Length)
return false;
for (int i = 0; i < parameterTypes.Length; i++) {
if (parameters[i].ParameterType != parameterTypes[i])
return false;
}
return true;
});
if ((object)methodInfo != null)
return methodInfo;
}
return null;
}
[return: System.Runtime.CompilerServices.Nullable(2)]
internal static PropertyInfo GetPublicInstanceProperty(this Type type, string name, Type[] indexParameterTypes)
{
Type type2 = type;
while ((object)type2 != null) {
PropertyInfo propertyInfo = type2.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public).SingleOrDefault(delegate(PropertyInfo candidate) {
if (candidate.Name != name)
return false;
ParameterInfo[] indexParameters = candidate.GetIndexParameters();
if (indexParameters.Length != indexParameterTypes.Length)
return false;
for (int i = 0; i < indexParameterTypes.Length; i++) {
if (indexParameters[i].ParameterType != indexParameterTypes[i])
return false;
}
return true;
});
if ((object)propertyInfo != null)
return propertyInfo;
type2 = type2.BaseType;
}
return null;
}
[System.Runtime.CompilerServices.NullableContext(2)]
internal static object InvokeWithTransparentExceptions([System.Runtime.CompilerServices.Nullable(1)] this MethodBase methodBase, object instance)
{
try {
return methodBase.Invoke(instance, null);
} catch (TargetInvocationException ex) {
ExceptionHelper.Rethrow(ex.InnerException);
throw new InvalidOperationException("ExceptionHelper.Rethrow failed to throw an exception.");
}
}
[return: System.Runtime.CompilerServices.Nullable(2)]
internal static object DynamicInvokeWithTransparentExceptions(this Delegate delegate)
{
try {
return delegate.DynamicInvoke(Array.Empty<object>());
} catch (TargetInvocationException ex) {
ExceptionHelper.Rethrow(ex.InnerException);
throw new InvalidOperationException("ExceptionHelper.Rethrow failed to throw an exception.");
}
}
internal static bool IsFSharpOption(this Type type, [System.Runtime.CompilerServices.Nullable(2)] [NotNullWhen(true)] out Type someType)
{
Guard.ArgumentNotNull(type, "type");
if (type.IsGenericType && type.GetGenericTypeDefinition().FullName == "Microsoft.FSharp.Core.FSharpOption`1") {
someType = type.GetGenericArguments()[0];
return true;
}
someType = null;
return false;
}
internal static bool IsVoidOrUnit(Type type)
{
Guard.ArgumentNotNull(type, "type");
if (!(type == typeof(void)))
return type.FullName == "Microsoft.FSharp.Core.Unit";
return true;
}
[return: System.Runtime.CompilerServices.Nullable(2)]
public static MethodInfo GetDefaultIndexer(Type type, Type[] indexerTypes)
{
string indexerName = GetIndexerName(type);
return type.GetProperty(indexerName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, null, indexerTypes, null)?.GetGetMethod(true);
}
private static string GetIndexerName(Type type)
{
return type.GetAttributes<DefaultMemberAttribute>(true).FirstOrDefault()?.MemberName ?? "Item";
}
public static MemberInfo[] GetMemberIncludingFromBase(this Type type, string name, BindingFlags flags)
{
Type type2 = type;
MemberInfo[] member;
do {
member = type2.GetMember(name, flags);
} while (member.Length == 0 && (type2 = type2.BaseType) != (Type)null);
return member;
}
}
}