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.

253 lines
13 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
  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 (potentially) private 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. /// <exception cref="InvalidOperationException">thrown when <paramref name="fieldName"/> is not a member of <paramref name="obj"/></exception>
  18. public static void SetPrivateField(this object obj, string fieldName, object value)
  19. {
  20. Type targetType = obj.GetType();
  21. obj.SetPrivateField(fieldName, value, targetType);
  22. }
  23. /// <summary>
  24. /// Sets a (potentially) private field on the target object. <paramref name="targetType"/> specifies the <see cref="Type"/> the field belongs to.
  25. /// </summary>
  26. /// <param name="obj">the object instance</param>
  27. /// <param name="fieldName">the field to set</param>
  28. /// <param name="value">the value to set it to</param>
  29. /// <param name="targetType">the object <see cref="Type"/> the field belongs to</param>
  30. /// <exception cref="InvalidOperationException">thrown when <paramref name="fieldName"/> is not a member of <paramref name="obj"/></exception>
  31. /// <exception cref="ArgumentException">thrown when <paramref name="obj"/> isn't assignable as <paramref name="targetType"/></exception>
  32. public static void SetPrivateField(this object obj, string fieldName, object value, Type targetType)
  33. {
  34. var prop = targetType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  35. if (prop == null)
  36. throw new InvalidOperationException($"{fieldName} is not a member of {targetType.Name}");
  37. prop.SetValue(obj, value);
  38. }
  39. /// <summary>
  40. /// Gets the value of a (potentially) private field.
  41. /// </summary>
  42. /// <typeparam name="T">the type of te field (result casted)</typeparam>
  43. /// <param name="obj">the object instance to pull from</param>
  44. /// <param name="fieldName">the name of the field to read</param>
  45. /// <returns>the value of the field</returns>
  46. /// <exception cref="InvalidOperationException">thrown when <paramref name="fieldName"/> is not a member of <paramref name="obj"/></exception>
  47. public static T GetPrivateField<T>(this object obj, string fieldName)
  48. {
  49. Type targetType = obj.GetType();
  50. return obj.GetPrivateField<T>(fieldName, targetType);
  51. }
  52. /// <summary>
  53. /// Gets the value of a (potentially) private field. <paramref name="targetType"/> specifies the <see cref="Type"/> the field belongs to.
  54. /// </summary>
  55. /// <typeparam name="T">the type of the field (result casted)</typeparam>
  56. /// <param name="obj">the object instance to pull from</param>
  57. /// <param name="fieldName">the name of the field to read</param>
  58. /// <param name="targetType">the object <see cref="Type"/> the field belongs to</param>
  59. /// <returns>the value of the field</returns>
  60. /// <exception cref="InvalidOperationException">thrown when <paramref name="fieldName"/> is not a member of <paramref name="obj"/></exception>
  61. /// <exception cref="ArgumentException">thrown when <paramref name="obj"/> isn't assignable as <paramref name="targetType"/></exception>
  62. public static T GetPrivateField<T>(this object obj, string fieldName, Type targetType)
  63. {
  64. var prop = targetType.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
  65. if (prop == null)
  66. throw new InvalidOperationException($"{fieldName} is not a member of {targetType.Name}");
  67. var value = prop.GetValue(obj);
  68. return (T)value;
  69. }
  70. /// <summary>
  71. /// Sets a (potentially) private property on the target object.
  72. /// </summary>
  73. /// <param name="obj">the target object instance</param>
  74. /// <param name="propertyName">the name of the property</param>
  75. /// <param name="value">the value to set it to</param>
  76. /// <exception cref="InvalidOperationException">thrown when <paramref name="propertyName"/> is not a member of <paramref name="obj"/></exception>
  77. public static void SetPrivateProperty(this object obj, string propertyName, object value)
  78. {
  79. Type targetType = obj.GetType();
  80. obj.SetPrivateProperty(propertyName, value, targetType);
  81. }
  82. /// <summary>
  83. /// Sets a (potentially) private property on the target object.
  84. /// </summary>
  85. /// <param name="obj">the target object instance</param>
  86. /// <param name="propertyName">the name of the property</param>
  87. /// <param name="value">the value to set it to</param>
  88. /// <param name="targetType">the object <see cref="Type"/> the property belongs to</param>
  89. /// <exception cref="InvalidOperationException">thrown when <paramref name="propertyName"/> is not a member of <paramref name="obj"/></exception>
  90. /// <exception cref="ArgumentException">thrown when <paramref name="obj"/> isn't assignable as <paramref name="targetType"/></exception>
  91. public static void SetPrivateProperty(this object obj, string propertyName, object value, Type targetType)
  92. {
  93. var prop = targetType
  94. .GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  95. if (prop == null)
  96. throw new InvalidOperationException($"{propertyName} is not a member of {targetType.Name}");
  97. prop.SetValue(obj, value, null);
  98. }
  99. /// <summary>
  100. /// Invokes a (potentially) private method.
  101. /// </summary>
  102. /// <param name="obj">the object to call from</param>
  103. /// <param name="methodName">the method name</param>
  104. /// <param name="methodParams">the method parameters</param>
  105. /// <returns>the return value</returns>
  106. /// <exception cref="InvalidOperationException">thrown when <paramref name="methodName"/> is not a member of <paramref name="obj"/></exception>
  107. public static object InvokePrivateMethod(this object obj, string methodName, params object[] methodParams)
  108. {
  109. Type targetType = obj.GetType();
  110. MethodInfo dynMethod = targetType.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
  111. if (dynMethod == null)
  112. throw new InvalidOperationException($"{methodName} is not a member of {targetType.Name}");
  113. return dynMethod.Invoke(obj, methodParams);
  114. }
  115. /// <summary>
  116. /// Invokes a (potentially) private method.
  117. /// </summary>
  118. /// <typeparam name="T">the return type</typeparam>
  119. /// <param name="obj">the object to call from</param>
  120. /// <param name="methodName">the method name to call</param>
  121. /// <param name="methodParams">the method's parameters</param>
  122. /// <returns>the return value</returns>
  123. public static T InvokePrivateMethod<T>(this object obj, string methodName, params object[] methodParams)
  124. {
  125. return (T)InvokePrivateMethod(obj, methodName, methodParams);
  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 | BindingFlags.GetField);
  172. foreach (FieldInfo fi in myObjectFields)
  173. {
  174. fi.SetValue(destination, fi.GetValue(source));
  175. }
  176. }
  177. /// <summary>
  178. /// Calls an instance method on a type specified by <paramref name="functionClass"/> and <paramref name="dependency"/>.
  179. /// </summary>
  180. /// <seealso cref="CallNonStaticMethod(Type, string, Type[], object[])"/>
  181. /// <param name="functionClass">the type name</param>
  182. /// <param name="dependency">the assembly the type is in</param>
  183. /// <param name="function">the name of the method to call</param>
  184. /// <param name="methodSig">the type signature of the method</param>
  185. /// <param name="parameters">the method parameters</param>
  186. /// <returns>the result of the call</returns>
  187. public static object CallNonStaticMethod(string functionClass, string dependency, string function, Type[] methodSig, params object[] parameters)
  188. {
  189. return CallNonStaticMethod(Type.GetType(string.Format("{0},{1}", functionClass, dependency)), function, methodSig, parameters);
  190. }
  191. /// <summary>
  192. /// Calls an instance method on a new object.
  193. /// </summary>
  194. /// <param name="type">the object type</param>
  195. /// <param name="function">the name of the method to call</param>
  196. /// <param name="methodSig">the type signature</param>
  197. /// <param name="parameters">the parameters</param>
  198. /// <returns>the result of the call</returns>
  199. public static object CallNonStaticMethod(this Type type, /*string functionClass, string dependency,*/ string function, Type[] methodSig, params object[] parameters)
  200. {
  201. //Type FunctionClass = Type.GetType(string.Format("{0},{1}", functionClass, dependency));
  202. if (type != null)
  203. {
  204. object instance = Activator.CreateInstance(type);
  205. {
  206. Type instType = instance.GetType();
  207. MethodInfo methodInfo = instType.GetMethod(function, methodSig);
  208. if (methodInfo != null)
  209. {
  210. return methodInfo.Invoke(instance, parameters);
  211. }
  212. throw new Exception("Method not found");
  213. }
  214. }
  215. throw new ArgumentNullException(nameof(type));
  216. }
  217. /// <summary>
  218. /// Calls an instance method on a new object.
  219. /// </summary>
  220. /// <seealso cref="CallNonStaticMethod(Type, string, Type[], object[])"/>
  221. /// <typeparam name="T">the return type</typeparam>
  222. /// <param name="type">the object type</param>
  223. /// <param name="function">the name of the method to call</param>
  224. /// <param name="methodSig">the type signature</param>
  225. /// <param name="parameters">the parameters</param>
  226. /// <returns>the result of the call</returns>
  227. public static T CallNonStaticMethod<T>(this Type type, string function, Type[] methodSig, params object[] parameters)
  228. {
  229. return (T)CallNonStaticMethod(type, function, methodSig, parameters);
  230. }
  231. }
  232. }