You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

195 lines
9.2 KiB

6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
  1. using System;
  2. using System.Reflection;
  3. using UnityEngine;
  4. namespace IPA.Utilities
  5. {
  6. /// <summary>
  7. /// A utility class providing reflection helper methods.
  8. /// </summary>
  9. public static class ReflectionUtil
  10. {
  11. /// <summary>
  12. /// Sets a field on the target object.
  13. /// </summary>
  14. /// <param name="obj">the object instance</param>
  15. /// <param name="fieldName">the field to set</param>
  16. /// <param name="value">the value to set it to</param>
  17. public static void SetField(this object obj, string fieldName, object value)
  18. {
  19. var prop = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  20. if (prop == null) throw new ArgumentException($"Field {fieldName} does not exist", nameof(fieldName));
  21. prop?.SetValue(obj, value);
  22. }
  23. /// <summary>
  24. /// Sets a field on the target object, as gotten from <typeparamref name="T"/>.
  25. /// </summary>
  26. /// <typeparam name="T">the type to get the field from</typeparam>
  27. /// <param name="obj">the object instance</param>
  28. /// <param name="fieldName">the field to set</param>
  29. /// <param name="value">the value to set it to</param>
  30. public static void SetField<T>(this T obj, string fieldName, object value) where T : class
  31. {
  32. var prop = typeof(T).GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  33. if (prop == null) throw new ArgumentException($"Field {fieldName} does not exist", nameof(fieldName));
  34. prop?.SetValue(obj, value);
  35. }
  36. /// <summary>
  37. /// Gets the value of a field.
  38. /// </summary>
  39. /// <typeparam name="T">the type of the field (result casted)</typeparam>
  40. /// <param name="obj">the object instance to pull from</param>
  41. /// <param name="fieldName">the name of the field to read</param>
  42. /// <returns>the value of the field</returns>
  43. public static T GetField<T>(this object obj, string fieldName)
  44. {
  45. var prop = obj.GetType().GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
  46. if (prop == null) throw new ArgumentException($"Field {fieldName} does not exist", nameof(fieldName));
  47. var value = prop?.GetValue(obj);
  48. return (T) value;
  49. }
  50. /// <summary>
  51. /// Sets a property on the target object.
  52. /// </summary>
  53. /// <param name="obj">the target object instance</param>
  54. /// <param name="propertyName">the name of the property</param>
  55. /// <param name="value">the value to set it to</param>
  56. public static void SetProperty(this object obj, string propertyName, object value)
  57. {
  58. var prop = obj.GetType().GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  59. if (prop == null) throw new ArgumentException($"Property {propertyName} does not exist", nameof(propertyName));
  60. prop?.SetValue(obj, value, null);
  61. }
  62. /// <summary>
  63. /// Sets a property on the target object, as gotten from <typeparamref name="T"/>
  64. /// </summary>
  65. /// <typeparam name="T">the type to get the property from</typeparam>
  66. /// <param name="obj">the object instance</param>
  67. /// <param name="propertyName">the property to set</param>
  68. /// <param name="value">the value to set it to</param>
  69. public static void SetProperty<T>(this T obj, string propertyName, object value) where T : class
  70. {
  71. var prop = typeof(T).GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  72. if (prop == null) throw new ArgumentException($"Property {propertyName} does not exist", nameof(propertyName));
  73. prop?.SetValue(obj, value, null);
  74. }
  75. /// <summary>
  76. /// Invokes a method on an object.
  77. /// </summary>
  78. /// <param name="obj">the object to call from</param>
  79. /// <param name="methodName">the method name</param>
  80. /// <param name="methodArgs">the method arguments</param>
  81. /// <returns>the return value</returns>
  82. public static object InvokeMethod(this object obj, string methodName, params object[] methodArgs)
  83. {
  84. MethodInfo dynMethod = obj.GetType().GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  85. if (dynMethod == null) throw new ArgumentException($"Method {methodName} does not exist", nameof(methodName));
  86. return dynMethod?.Invoke(obj, methodArgs);
  87. }
  88. /// <summary>
  89. /// Invokes a method from <typeparamref name="T"/> on an object.
  90. /// </summary>
  91. /// <typeparam name="T">the type to search for the method on</typeparam>
  92. /// <param name="obj">the object instance</param>
  93. /// <param name="methodName">the method's name</param>
  94. /// <param name="args">the method arguments</param>
  95. /// <returns>the return value</returns>
  96. public static object InvokeMethod<T>(this T obj, string methodName, params object[] args) where T : class
  97. {
  98. var dynMethod = typeof(T).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  99. if (dynMethod == null) throw new ArgumentException($"Method {methodName} does not exist", nameof(methodName));
  100. return dynMethod?.Invoke(obj, args);
  101. }
  102. /// <summary>
  103. /// Invokes a method.
  104. /// </summary>
  105. /// <typeparam name="T">the return type</typeparam>
  106. /// <param name="obj">the object instance</param>
  107. /// <param name="methodName">the method name to call</param>
  108. /// <param name="methodArgs">the method's arguments</param>
  109. /// <returns>the return value</returns>
  110. public static T InvokeMethod<T>(this object obj, string methodName, params object[] methodArgs)
  111. {
  112. return (T)InvokeMethod(obj, methodName, methodArgs);
  113. }
  114. /// <summary>
  115. /// Invokes a method from <typeparamref name="U"/> on an object.
  116. /// </summary>
  117. /// <typeparam name="T">the return type</typeparam>
  118. /// <typeparam name="U">the type to search for the method on</typeparam>
  119. /// <param name="obj">the object instance</param>
  120. /// <param name="methodName">the method name to call</param>
  121. /// <param name="methodArgs">the method's arguments</param>
  122. /// <returns>the return value</returns>
  123. public static T InvokeMethod<T, U>(this U obj, string methodName, params object[] methodArgs) where U : class
  124. {
  125. return (T)obj.InvokeMethod(methodName, methodArgs);
  126. }
  127. /// <summary>
  128. /// Copies a component <paramref name="original"/> to a component of <paramref name="overridingType"/> on the destination <see cref="GameObject"/>.
  129. /// </summary>
  130. /// <param name="original">the original component</param>
  131. /// <param name="overridingType">the new component's type</param>
  132. /// <param name="destination">the destination GameObject</param>
  133. /// <param name="originalTypeOverride">overrides the source component type (for example, to a superclass)</param>
  134. /// <returns>the copied component</returns>
  135. public static Component CopyComponent(this Component original, Type overridingType, GameObject destination, Type originalTypeOverride = null)
  136. {
  137. var copy = destination.AddComponent(overridingType);
  138. var originalType = originalTypeOverride ?? original.GetType();
  139. Type type = originalType;
  140. while (type != typeof(MonoBehaviour))
  141. {
  142. CopyForType(type, original, copy);
  143. type = type?.BaseType;
  144. }
  145. return copy;
  146. }
  147. /// <summary>
  148. /// A generic version of <see cref="CopyComponent(Component, Type, GameObject, Type)"/>.
  149. /// </summary>
  150. /// <seealso cref="CopyComponent(Component, Type, GameObject, Type)"/>
  151. /// <typeparam name="T">the overriding type</typeparam>
  152. /// <param name="original">the original component</param>
  153. /// <param name="destination">the destination game object</param>
  154. /// <param name="originalTypeOverride">overrides the source component type (for example, to a superclass)</param>
  155. /// <returns>the copied component</returns>
  156. public static T CopyComponent<T>(this Component original, GameObject destination, Type originalTypeOverride = null)
  157. where T : Component
  158. {
  159. var copy = destination.AddComponent<T>();
  160. var originalType = originalTypeOverride ?? original.GetType();
  161. Type type = originalType;
  162. while (type != typeof(MonoBehaviour))
  163. {
  164. CopyForType(type, original, copy);
  165. type = type?.BaseType;
  166. }
  167. return copy;
  168. }
  169. private static void CopyForType(Type type, Component source, Component destination)
  170. {
  171. FieldInfo[] myObjectFields = type.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  172. foreach (FieldInfo fi in myObjectFields)
  173. {
  174. fi.SetValue(destination, fi.GetValue(source));
  175. }
  176. }
  177. }
  178. }