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; } } } }