using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Reflection.Emit; namespace Vanara.Extensions.Reflection { /// Extensions for related to System.Reflection public static class ReflectionExtensions { private const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase; /// Get a sequence of types that represent all base types and interfaces. /// The type to evaluate. /// A sequence of types that represent all base types and interfaces. public static IEnumerable EnumInheritance(this Type type) { foreach (var i in type.GetInterfaces()) yield return i; while (type.BaseType != null) yield return type = type.BaseType; } /// Searches for the constants defined for , using the specified binding constraints. /// The type to search. /// A bitwise combination of the enumeration values that specify how the search is conducted. /// /// /// A sequence of objects representing all fields defined for that match the /// specified binding constraints. /// /// -or- /// /// An empty sequence of type , if no fields are defined for , or if none of the /// defined fields match the binding constraints. /// /// public static IEnumerable GetConstants(this Type type, BindingFlags bindingFlags = BindingFlags.Public) => type.GetFields(BindingFlags.Static | bindingFlags).Where(fi => fi.IsLiteral && !fi.IsInitOnly); /// Gets a named field value from an object. /// The expected type of the field to be returned. /// The object from which to retrieve the field. /// Name of the field. /// The field value. public static T GetFieldValue(this object obj, string fieldName) { if (obj is null) throw new ArgumentNullException(nameof(obj)); if (string.IsNullOrEmpty(fieldName)) throw new ArgumentNullException(nameof(fieldName)); return (T)obj.GetType().InvokeMember(fieldName, BindingFlags.GetField | bindingFlags, null, obj, null, null); } /// Gets a named field value from an object. /// The expected type of the field to be returned. /// The object from which to retrieve the field. /// Name of the field. /// The default value to return in the instance that the field is not found. /// The field value, if found, or the if not. public static T GetFieldValue(this object obj, string fieldName, T defaultValue) { try { return GetFieldValue(obj, fieldName); } catch { return defaultValue; } } /// Gets a named property value from an object. /// The expected type of the property to be returned. /// The object from which to retrieve the property. /// Name of the property. /// The property value. public static T GetPropertyValue(this object obj, string propertyName) { if (obj is null) throw new ArgumentNullException(nameof(obj)); return (T)obj.GetType().InvokeMember(propertyName, BindingFlags.GetProperty | bindingFlags, null, obj, null, null); } /// Gets a named property value from an object. /// The expected type of the property to be returned. /// The object from which to retrieve the property. /// Name of the property. /// The default value to return in the instance that the property is not found. /// The property value, if found, or the if not. public static T GetPropertyValue(this object obj, string propertyName, T defaultValue) { try { return GetPropertyValue(obj, propertyName); } catch { return defaultValue; } } /// Determines if a type inherits from another type. The may be a generic type definition. /// The type. /// The base type. /// /// if is found in the inheritance list for ; /// otherwise. /// public static bool InheritsFrom(this Type type, Type baseType) { return !(type is null) && !(baseType is null) || type.BaseType != null && type.BaseType.IsGenericType && type.BaseType.GetGenericTypeDefinition().Equals(baseType) || type.BaseType.InheritsFrom(baseType) || baseType.IsAssignableFrom(type) || type.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition().Equals(baseType)); } /// Determines if a type inherits from another type. The may be a generic type definition. /// The base type. /// The type. /// /// if is found in the inheritance list for ; /// otherwise. /// public static bool InheritsFrom(this Type type) => type.InheritsFrom(typeof(T)); /// /// Invokes a generic named method on an object with parameters and no return value. /// /// The object on which to invoke the method or constructor. If a method is static, this argument is ignored. If a constructor is static, this argument must be null or an instance of the class that defines the constructor. /// The string containing the name of the public method to get. /// An array of types to be substituted for the type parameters of the current generic method definition. /// An array of Type objects representing the number, order, and type of the parameters for the method to get. /// -or- /// An empty array of Type objects(as provided by the EmptyTypes field) to get a method that takes no parameters. /// An argument list for the invoked method or constructor. This is an array of objects with the same number, order, and type as the parameters of the method or constructor to be invoked. If there are no parameters, this should be null. /// An Object containing the return value of the invoked method, or null in the case of a constructor, or null if the method's return type is void. Before calling the method or constructor, Invoke checks to see if the user has access permission and verifies that the parameters are valid. /// Method not found - methodName public static object InvokeGenericMethod(this object obj, string methodName, Type[] typeArguments, Type[] argTypes, object[] args) { var mi = obj?.GetType().GetMethod(methodName, bindingFlags, null, argTypes, null); if (mi == null) throw new ArgumentException(@"Method not found", nameof(methodName)); var gmi = mi.MakeGenericMethod(typeArguments); return gmi.Invoke(obj, args); } /// Invokes a named method on an object with parameters and no return value. /// The object on which to invoke the method. /// Name of the method. /// The arguments to provide to the method invocation. public static void InvokeMethod(this object obj, string methodName, params object[] args) { var argTypes = args == null || args.Length == 0 ? Type.EmptyTypes : Array.ConvertAll(args, o => o?.GetType() ?? typeof(object)); InvokeMethod(obj, methodName, argTypes, args); } /// Invokes a named method on an object with parameters and no return value. /// The expected type of the method's return value. /// The object on which to invoke the method. /// Name of the method. /// The arguments to provide to the method invocation. /// The value returned from the method. public static T InvokeMethod(this object obj, string methodName, params object[] args) { var argTypes = args == null || args.Length == 0 ? Type.EmptyTypes : Array.ConvertAll(args, o => o?.GetType() ?? typeof(object)); return InvokeMethod(obj, methodName, argTypes, args); } /// Invokes a named method on an object with parameters and no return value. /// The object on which to invoke the method. /// Name of the method. /// The argument types. /// The arguments to provide to the method invocation. public static void InvokeMethod(this object obj, string methodName, Type[] argTypes, object[] args) { var mi = obj?.GetType().GetMethod(methodName, bindingFlags, null, argTypes, null); if (mi == null) throw new ArgumentException(@"Method not found", nameof(methodName)); mi.Invoke(obj, args); } /// Invokes a named method on an object with parameters and no return value. /// The expected type of the method's return value. /// The object on which to invoke the method. /// Name of the method. /// The argument types. /// The arguments to provide to the method invocation. /// The value returned from the method. public static T InvokeMethod(this object obj, string methodName, Type[] argTypes, object[] args) { var mi = obj?.GetType().GetMethod(methodName, bindingFlags, null, argTypes, null); if (mi == null) throw new ArgumentException(@"Method not found", nameof(methodName)); var tt = typeof(T); if (tt != typeof(object) && mi.ReturnType != tt && !mi.ReturnType.IsSubclassOf(tt)) throw new ArgumentException(@"Return type mismatch", nameof(T)); return (T)mi.Invoke(obj, args); } /// Sets a named field on an object. /// The type of the field to be set. /// The type of the object on which to the set the field. /// The object on which to set the field. /// Name of the field. /// The field value to set on the object. public static void SetFieldValue(this TS obj, string fieldName, T value) where TS : class { try { obj?.GetType().InvokeMember(fieldName, BindingFlags.SetField | bindingFlags, null, obj, new object[] { value }, null); } catch { } } /// Sets a named field on an object. /// The type of the field to be set. /// The type of the structure on which to the set the field. /// The object on which to set the field. /// Name of the field. /// The field value to set on the object. public static void SetFieldValue(this ref TS obj, string fieldName, T value) where TS : struct { var tr = __makeref(obj); var fi = typeof(TS).GetField(fieldName, bindingFlags); if (fi is null) throw new MissingFieldException(typeof(TS).Name, fieldName); fi.SetValueDirect(tr, value); } /// Sets a named property on an object. /// The type of the property to be set. /// The object on which to set the property. /// Name of the property. /// The property value to set on the object. public static void SetPropertyValue(this object obj, string propName, T value) { try { obj?.GetType().InvokeMember(propName, BindingFlags.SetProperty | bindingFlags, null, obj, new object[] { value }, null); } catch { } } } } namespace Vanara.Extensions { using Vanara.Extensions.Reflection; /// Extensions related to System.Reflection public static class ReflectionExtensions { private const BindingFlags staticBindingFlags = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase; /// For a structure, gets either the result of the static Create method or the default. /// The structure's type. /// The result of the static Create method or the default. public static T CreateOrDefault() where T : struct { var mi = typeof(T).GetMethod("Create", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static, null, Type.EmptyTypes, null); if (mi != null) return (T)mi.Invoke(null, null); var fi = typeof(T).GetField("Default", BindingFlags.Public | BindingFlags.Static); if (fi != null) return (T)fi.GetValue(null); var pi = typeof(T).GetProperty("Default", BindingFlags.Public | BindingFlags.Static | BindingFlags.GetProperty, null, typeof(T), Type.EmptyTypes, null); if (pi != null) return (T)pi.GetValue(null); return default; } /// Gets all loaded types in the . /// The application domain. /// All loaded types. public static IEnumerable GetAllTypes(this AppDomain appDomain) => appDomain.GetAssemblies().SelectMany(a => a.GetTypes()); /// Returns an array of custom attributes applied to this member and identified by . /// The type of attribute to search for. Only attributes that are assignable to this type are returned. /// An object derived from the MemberInfo class that describes a constructor, event, field, method, or property member of a class. /// true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events. /// An optional predicate to refine the results. /// public static IEnumerable GetCustomAttributes(this MemberInfo element, bool inherit = false, Func predicate = null) where TAttr : Attribute => element.GetCustomAttributes(typeof(TAttr), inherit).Cast().Where(predicate ?? (a => true)); /// Returns an array of custom attributes applied to this member and identified by . /// The type of attribute to search for. Only attributes that are assignable to this type are returned. /// The type of the to examine. /// true to search this member's inheritance chain to find the attributes; otherwise, false. This parameter is ignored for properties and events. /// An optional predicate to refine the results. /// public static IEnumerable GetCustomAttributes(this Type type, bool inherit = false, Func predicate = null) where TAttr : Attribute => type.GetCustomAttributes(typeof(TAttr), inherit).Cast().Where(predicate ?? (a => true)); /// Finds the type of the element of a type. Returns null if this type does not enumerate. /// The type to check. /// The element type, if found; otherwise, . public static Type FindElementType(this Type type) { if (type.IsArray) return type.GetElementType(); // type is IEnumerable; if (ImplIEnumT(type)) return type.GetGenericArguments().First(); // type implements/extends IEnumerable; var enumType = type.GetInterfaces().Where(ImplIEnumT).Select(t => t.GetGenericArguments().First()).FirstOrDefault(); if (enumType != null) return enumType; // type is IEnumerable if (IsIEnum(type) || type.GetInterfaces().Any(IsIEnum)) return typeof(object); return null; bool IsIEnum(Type t) => t == typeof(System.Collections.IEnumerable); bool ImplIEnumT(Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IEnumerable<>); } /// Gets the fields of a structure with sequential layout in the order in which they appear in memory. /// The type of the structure. /// The binding flags. /// An ordered sequence of instances representing the fields in the structure. public static IEnumerable GetOrderedFields(this Type type, BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.Instance) => type.GetFields(bindingFlags).Select(fi => (System.Runtime.InteropServices.Marshal.OffsetOf(type, fi.Name).ToInt64(), fi)).OrderBy(t => t.Item1).Select(t => t.fi); /// Gets a named field value from an object. /// The expected type of the field to be returned. /// Name of the field. /// The field value. public static T GetStaticFieldValue(string fieldName) { if (string.IsNullOrEmpty(fieldName)) throw new ArgumentNullException(nameof(fieldName)); return (T)typeof(T).InvokeMember(fieldName, BindingFlags.GetField | staticBindingFlags, null, null, null); } /// Invokes a named method on a created instance of a type with parameters. /// The expected type of the method's return value. /// The type to be instantiated and then used to invoke the method. This method assumes the type has a default public constructor. /// Name of the method. /// The arguments to provide to the method invocation. /// The value returned from the method. public static T InvokeMethod(this Type type, string methodName, params object[] args) { var o = Activator.CreateInstance(type); return o.InvokeMethod(methodName, args); } /// Invokes a named method on a created instance of a type with parameters. /// The expected type of the method's return value. /// The type to be instantiated and then used to invoke the method. /// The arguments to supply to the constructor. /// Name of the method. /// The arguments to provide to the method invocation. /// The value returned from the method. public static T InvokeMethod(this Type type, object[] instArgs, string methodName, params object[] args) { var o = Activator.CreateInstance(type, instArgs); return o.InvokeMethod(methodName, args); } /// Invokes a named static method of a type with parameters. /// The expected type of the method's return value. /// The type containing the static method. /// Name of the method. /// The arguments to provide to the method invocation. /// The value returned from the method. public static T InvokeStaticMethod(this Type type, string methodName, params object[] args) { var argTypes = args == null || args.Length == 0 ? Type.EmptyTypes : Array.ConvertAll(args, o => o?.GetType() ?? typeof(object)); var mi = type.GetMethod(methodName, staticBindingFlags, null, argTypes, null); if (mi == null) throw new ArgumentException(@"Method not found", nameof(methodName)); return (T)mi.Invoke(null, args); } #if !NETSTANDARD2_0 /// Invokes a method from a derived base class. /// The instance from the derived class for the method to invoke. /// The target object. /// The arguments. /// The value returned from the method. public static object InvokeNotOverride(this MethodInfo methodInfo, object targetObject, params object[] arguments) { var parameters = methodInfo.GetParameters(); if (parameters.Length != arguments.Length) throw new Exception("Arguments count doesn't match"); Type returnType = null; if (methodInfo.ReturnType != typeof(void)) returnType = methodInfo.ReturnType; var type = targetObject.GetType(); var dynamicMethod = new DynamicMethod("", returnType, new[] { type, typeof(object) }, type); var iLGenerator = dynamicMethod.GetILGenerator(); iLGenerator.Emit(OpCodes.Ldarg_0); // this for (var i = 0; i < parameters.Length; i++) { var parameter = parameters[i]; iLGenerator.Emit(OpCodes.Ldarg_1); // load array argument // get element at index iLGenerator.Emit(OpCodes.Ldc_I4_S, i); // specify index iLGenerator.Emit(OpCodes.Ldelem_Ref); // get element var parameterType = parameter.ParameterType; iLGenerator.Emit(OpCodes.Unbox_Any, parameterType); } iLGenerator.Emit(OpCodes.Call, methodInfo); iLGenerator.Emit(OpCodes.Ret); return dynamicMethod.Invoke(null, new[] { targetObject, arguments }); } #endif /// Determines whether the specified method is compatible with a delegate. /// The type of the delegate. /// The method information. /// if method is compatible with the delegate; otherwise, . public static bool IsMethodCompatibleWithDelegate(this MethodInfo method) where TDel : Delegate { var delegateSignature = typeof(TDel).GetMethod("Invoke"); return delegateSignature.ReturnType == method.ReturnType && delegateSignature.GetParameters().Select(x => x.ParameterType).SequenceEqual(method.GetParameters().Select(x => x.ParameterType)); } /// Loads a type from a named assembly. /// Name of the type. /// The name or path of the file that contains the manifest of the assembly. /// The reference, or null if type or assembly not found. public static Type LoadType(string typeName, string asmRef = null) { Assembly asm = null; try { asm = Assembly.LoadFrom(asmRef); } catch { } if (!TryGetType(asm, typeName, out var ret)) { foreach (var asm2 in AppDomain.CurrentDomain.GetAssemblies()) if (TryGetType(asm2, typeName, out ret)) break; } return ret; } /// Tries the retrieve a reference from an assembly. /// Name of the type. /// The assembly from which to load the type. /// The reference, if found. /// true if the type was found in the assembly; otherwise, false. private static bool TryGetType(Assembly asm, string typeName, out Type type) { type = asm?.GetType(typeName, false, false); return !(type is null); } } }