using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using RPGCoreCommon.Helpers.Exceptions;
namespace RPGCoreCommon.Helpers
{
public static class ReflectionExtensions
{
public const BindingFlags DefaultBindingFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase;
/// Works with any member (type, field, property, method)
/// type of this member's value
public static Type GetMemberValueType(this MemberInfo memberInfo)
{
return memberInfo switch
{
Type type => type,
FieldInfo fieldInfo => fieldInfo.FieldType,
PropertyInfo propertyInfo => propertyInfo.PropertyType,
MethodInfo methodInfo => methodInfo.ReturnType,
_ => null
};
}
/// Works with any member (type, field, property, method)
/// this member
/// Object to use for this reflection
/// member's value
public static object GetMemberValue(this MemberInfo memberInfo, object obj)
{
return memberInfo switch
{
Type => obj,
FieldInfo fi => fi.GetValue(obj),
PropertyInfo pi => pi.GetValue(obj, null),
MethodInfo mi => mi.Invoke(obj, null),
_ => null
};
}
/// Works with any member (type, field, property, method)
/// this member
/// path from current member with dot "." as delimiter
/// Custom binding flags to use
/// TYPE of member's value from given path
/// when member is not found
public static Type GetMemberValueTypeByPath(this MemberInfo memberInfo, string path, BindingFlags bindingFlags = DefaultBindingFlags)
{
var pathParts = path.Split('.');
var memberType = memberInfo.GetMemberValueType();
foreach (var pathPart in pathParts)
{
var member = memberType.GetMember(pathPart, bindingFlags).FirstOrDefault();
if (member == null) throw new MemberNotFoundException(path, memberType.GetMemberValueType());
memberType = member.GetMemberValueType();
}
return memberType;
}
/// This function supports all members: type, property, field, method
/// Type (or any memberInfo) that will be used to get result value. When not provided memberInfo will be taken from obj type
/// Object from which finding will be performed
/// path from current member with dot "." as delimiter
/// Custom binding flags to use
/// member's value from given path
/// When property at given path doesn't exist
public static object GetMemberValueByPath(this MemberInfo memberInfo, object obj, string path, BindingFlags bindingFlags = DefaultBindingFlags)
{
var pathParts = path.Split('.');
var memberType = memberInfo.GetMemberValueType();
var memberObj = obj;
foreach (var pathPart in pathParts)
{
var member = memberType.GetMember(pathPart, bindingFlags).FirstOrDefault();
if (member == null) throw new MemberNotFoundException(path, memberType.GetMemberValueType());
memberType = member.GetMemberValueType();
memberObj = memberType.GetMemberValue(memberObj);
}
return memberObj;
}
///
/// When result is not desired type
/// Result type
public static T GetMemberValueByPath(this MemberInfo memberInfo, object obj, string path, BindingFlags bindingFlags = DefaultBindingFlags)
{
var result = GetMemberValueByPath(memberInfo, obj, path, bindingFlags);
if (result is T typedResult) return typedResult;
throw new MemberValueWrongTypeException(path, obj.GetType(), typeof(T), result.GetType());
}
///
public static object GetMemberValueByPath(object obj, string path, BindingFlags bindingFlags = DefaultBindingFlags)
{
return GetMemberValueByPath(obj.GetType(), obj, path, bindingFlags);
}
///
/// Tries to find specific generic type in currently implementation.
///
/// This type
/// Generic type to find
/// Found generic type with parameters
/// Generic type to find isn't generic.
public static Type FindGenericDefinitionType(this Type type, Type genericTypeDefinition)
{
if (!genericTypeDefinition.IsGenericTypeDefinition) throw new Exception("The generic argument must be a generic type definition.");
var tempType = type;
while (tempType != null && tempType != typeof(object))
{
if (tempType.IsGenericType && tempType.GetGenericTypeDefinition() == genericTypeDefinition) return tempType;
tempType = tempType.BaseType;
}
return null;
}
///
/// Checks if specific generic type in currently implementation has type as argument at index.
///
/// this type
/// Generic type to find
/// index to check
/// type in generic implementation
///
public static bool IsArgTypeInGenericDefinitionType(this Type type, Type genericTypeDefinition, int argIndex, Type argType)
{
return type.FindGenericDefinitionType(genericTypeDefinition).GenericTypeArguments.ElementAtOrDefault(argIndex) == argType;
}
///
/// Checks if this member has public read accessor.
///
/// this member
/// TRUE if member exists and is public
public static bool IsMemberReadPublic(this MemberInfo memberInfo)
{
return memberInfo switch
{
Type type => type.IsPublic,
PropertyInfo pi => pi.GetGetMethod() != null,
MethodInfo mi => mi.IsPublic,
FieldInfo fi => fi.IsPublic,
_ => false
};
}
public static IEnumerable GetAllTypes(this Type forType, bool withObject = false)
{
var tempType = forType;
while (tempType is not null)
{
yield return tempType;
tempType = tempType.BaseType;
if (!withObject && tempType == typeof(object)) yield break;
}
}
}
}