using System; using System.Reflection; using System.Reflection.Emit; using UnityEngine; namespace IPA.Utilities { /// /// A utility class providing reflection helper methods. /// public static partial class ReflectionUtil { internal static readonly FieldInfo DynamicMethodReturnType = typeof(DynamicMethod).GetField("returnType", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); /// /// Sets a field on the target object, as gotten from . /// /// the type to get the field from /// the type of the field to set /// the object instance /// the field to set /// the value to set it to /// if does not exist on /// public static void SetField(this T obj, string fieldName, U value) => FieldAccessor.Set(ref obj, fieldName, value); /// /// Gets the value of a field. /// /// the type to get the field from /// the type of the field (result casted) /// the object instance to pull from /// the name of the field to read /// the value of the field /// if does not exist on /// public static U GetField(this T obj, string fieldName) => FieldAccessor.Get(ref obj, fieldName); /// /// Sets a property on the target object, as gotten from . /// /// the type to get the property from /// the type of the property to set /// the object instance /// the property to set /// the value to set it to /// if does not exist on /// public static void SetProperty(this T obj, string propertyName, U value) => PropertyAccessor.Set(ref obj, propertyName, value); /// /// Gets a property on the target object, as gotten from . /// /// the type to get the property from /// the type of the property to get /// the object instance /// the property to get /// the value of the property /// if does not exist on /// public static U GetProperty(this T obj, string propertyName) => PropertyAccessor.Get(ref obj, propertyName); /// /// Invokes a method from on an object. /// /// the type that the method returns /// the type to search for the method on /// the object instance /// the method's name /// the method arguments /// the return value /// if does not exist on public static U InvokeMethod(this T obj, string methodName, params object[] args) { var dynMethod = typeof(T).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (dynMethod == null) throw new MissingMethodException($"Method {methodName} does not exist", nameof(methodName)); return (U)dynMethod?.Invoke(obj, args); } /// /// Invokes a generic method from on an object. /// /// the type that the method returns /// the type to search for the method on /// the generic type to invoke the method with /// the object instance /// the method's name /// the method arguments /// the return value /// if does not exist on public static U InvokeGenericMethod(this T obj, string methodName, params object[] args) { var dynMethod = typeof(T).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if (dynMethod == null) throw new MissingMethodException($"Method {methodName} does not exist", nameof(methodName)); var genMethod = dynMethod.MakeGenericMethod(typeof(G)); return (U)genMethod?.Invoke(obj, args); } /// /// Copies a component to a component of on the destination . /// /// the original component /// the new component's type /// the destination GameObject /// overrides the source component type (for example, to a superclass) /// the copied component public static Component CopyComponent(this Component original, Type overridingType, GameObject destination, Type originalTypeOverride = null) { var copy = destination.AddComponent(overridingType); var originalType = originalTypeOverride ?? original.GetType(); Type type = originalType; while (type != typeof(MonoBehaviour)) { CopyForType(type, original, copy); type = type?.BaseType; } return copy; } /// /// A generic version of . /// /// /// the overriding type /// the original component /// the destination game object /// overrides the source component type (for example, to a superclass) /// the copied component public static T CopyComponent(this Component original, GameObject destination, Type originalTypeOverride = null) where T : Component { var copy = destination.AddComponent(); var originalType = originalTypeOverride ?? original.GetType(); Type type = originalType; while (type != typeof(MonoBehaviour)) { CopyForType(type, original, copy); type = type?.BaseType; } return copy; } /// /// Converts the property name to the one of the compiler-generated backing field. /// This can be used for the field-based reflection when you want to set the value of a get-only property /// /// Name of the property /// Name of the backing field /// /// Only works for properties with compiler-generated backing fields. /// This is only a simple method and doesn't have any guarantees to work 100% of the time across different compilers/runtimes. /// See this link for more info. /// public static string ToCompilerGeneratedBackingField(string propertyName) => $"<{propertyName}>k__BackingField"; private static void CopyForType(Type type, Component source, Component destination) { FieldInfo[] myObjectFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); foreach (FieldInfo fi in myObjectFields) { fi.SetValue(destination, fi.GetValue(source)); } } } }