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.

156 lines
8.2 KiB

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