ReflectionHelpers.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Reflection;
  6. using System.Text;
  7. namespace Cinemachine.Utility
  8. {
  9. /// <summary>An ad-hoc collection of helpers for reflection, used by Cinemachine
  10. /// or its editor tools in various places</summary>
  11. [DocumentationSorting(DocumentationSortingAttribute.Level.Undoc)]
  12. public static class ReflectionHelpers
  13. {
  14. /// <summary>Copy the fields from one object to another</summary>
  15. /// <param name="src">The source object to copy from</param>
  16. /// <param name="dst">The destination object to copy to</param>
  17. /// <param name="bindingAttr">The mask to filter the attributes.
  18. /// Only those fields that get caught in the filter will be copied</param>
  19. public static void CopyFields(
  20. System.Object src, System.Object dst,
  21. System.Reflection.BindingFlags bindingAttr
  22. = System.Reflection.BindingFlags.Public
  23. | System.Reflection.BindingFlags.NonPublic
  24. | System.Reflection.BindingFlags.Instance)
  25. {
  26. if (src != null && dst != null)
  27. {
  28. Type type = src.GetType();
  29. FieldInfo[] fields = type.GetFields(bindingAttr);
  30. for (int i = 0; i < fields.Length; ++i)
  31. if (!fields[i].IsStatic)
  32. fields[i].SetValue(dst, fields[i].GetValue(src));
  33. }
  34. }
  35. /// <summary>Search the assembly for all types that match a predicate</summary>
  36. /// <param name="assembly">The assembly to search</param>
  37. /// <param name="predicate">The type to look for</param>
  38. /// <returns>A list of types found in the assembly that inherit from the predicate</returns>
  39. public static IEnumerable<Type> GetTypesInAssembly(
  40. Assembly assembly, Predicate<Type> predicate)
  41. {
  42. if (assembly == null)
  43. return null;
  44. Type[] types = new Type[0];
  45. try
  46. {
  47. types = assembly.GetTypes();
  48. }
  49. catch (Exception)
  50. {
  51. // Can't load the types in this assembly
  52. }
  53. types = (from t in types
  54. where t != null && predicate(t)
  55. select t).ToArray();
  56. return types;
  57. }
  58. /// <summary>Get a type from a name</summary>
  59. /// <param name="typeName">The name of the type to search for</param>
  60. /// <returns>The type matching the name, or null if not found</returns>
  61. public static Type GetTypeInAllDependentAssemblies(string typeName)
  62. {
  63. foreach (Type type in GetTypesInAllDependentAssemblies(t => t.Name == typeName))
  64. return type;
  65. return null;
  66. }
  67. /// <summary>Search all assemblies for all types that match a predicate</summary>
  68. /// <param name="predicate">The type to look for</param>
  69. /// <returns>A list of types found in the assembly that inherit from the predicate</returns>
  70. public static IEnumerable<Type> GetTypesInAllDependentAssemblies(Predicate<Type> predicate)
  71. {
  72. List<Type> foundTypes = new List<Type>(100);
  73. Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
  74. string definedIn = typeof(CinemachineComponentBase).Assembly.GetName().Name;
  75. foreach (Assembly assembly in assemblies)
  76. {
  77. // Note that we have to call GetName().Name. Just GetName() will not work.
  78. if ((!assembly.GlobalAssemblyCache)
  79. && ((assembly.GetName().Name == definedIn)
  80. || assembly.GetReferencedAssemblies().Any(a => a.Name == definedIn)))
  81. try
  82. {
  83. foreach (Type foundType in GetTypesInAssembly(assembly, predicate))
  84. foundTypes.Add(foundType);
  85. }
  86. catch (Exception) {} // Just skip uncooperative assemblies
  87. }
  88. return foundTypes;
  89. }
  90. #if false
  91. /// <summary>call GetTypesInAssembly() for all assemblies that match a predicate</summary>
  92. /// <param name="assemblyPredicate">Which assemblies to search</param>
  93. /// <param name="predicate">What type to look for</param>
  94. public static IEnumerable<Type> GetTypesInLoadedAssemblies(
  95. Predicate<Assembly> assemblyPredicate, Predicate<Type> predicate)
  96. {
  97. Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
  98. assemblies = assemblies.Where((Assembly assembly)
  99. => { return assemblyPredicate(assembly); }).OrderBy((Assembly ass)
  100. => { return ass.FullName; }).ToArray();
  101. List<Type> foundTypes = new List<Type>(100);
  102. foreach (Assembly assembly in assemblies)
  103. {
  104. foreach (Type foundType in GetTypesInAssembly(assembly, predicate))
  105. foundTypes.Add(foundType);
  106. }
  107. return foundTypes;
  108. }
  109. /// <summary>Is a type defined and visible</summary>
  110. /// <param name="fullname">Fullly-qualified type name</param>
  111. /// <returns>true if the type exists</returns>
  112. public static bool TypeIsDefined(string fullname)
  113. {
  114. Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
  115. foreach (Assembly assembly in assemblies)
  116. {
  117. try
  118. {
  119. foreach (var type in assembly.GetTypes())
  120. if (type.FullName == fullname)
  121. return true;
  122. }
  123. catch (System.Exception) {} // Just skip uncooperative assemblies
  124. }
  125. return false;
  126. }
  127. #endif
  128. /// <summary>Cheater extension to access internal field of an object</summary>
  129. /// <typeparam name="T">The field type</typeparam>
  130. /// <param name="type">The type of the field</param>
  131. /// <param name="obj">The object to access</param>
  132. /// <param name="memberName">The string name of the field to access</param>
  133. /// <returns>The value of the field in the objects</returns>
  134. public static T AccessInternalField<T>(this Type type, object obj, string memberName)
  135. {
  136. if (string.IsNullOrEmpty(memberName) || (type == null))
  137. return default(T);
  138. System.Reflection.BindingFlags bindingFlags = System.Reflection.BindingFlags.NonPublic;
  139. if (obj != null)
  140. bindingFlags |= System.Reflection.BindingFlags.Instance;
  141. else
  142. bindingFlags |= System.Reflection.BindingFlags.Static;
  143. FieldInfo field = type.GetField(memberName, bindingFlags);
  144. if ((field != null) && (field.FieldType == typeof(T)))
  145. return (T)field.GetValue(obj);
  146. else
  147. return default(T);
  148. }
  149. #if false
  150. /// <summary>Cheater extension to access internal property of an object</summary>
  151. /// <typeparam name="T">The field type</typeparam>
  152. /// <param name="type">The type of the field</param>
  153. /// <param name="obj">The object to access</param>
  154. /// <param name="memberName">The string name of the field to access</param>
  155. /// <returns>The value of the field in the objects</returns>
  156. public static T AccessInternalProperty<T>(this Type type, object obj, string memberName)
  157. {
  158. if (string.IsNullOrEmpty(memberName) || (type == null))
  159. return default(T);
  160. System.Reflection.BindingFlags bindingFlags = System.Reflection.BindingFlags.NonPublic;
  161. if (obj != null)
  162. bindingFlags |= System.Reflection.BindingFlags.Instance;
  163. else
  164. bindingFlags |= System.Reflection.BindingFlags.Static;
  165. PropertyInfo pi = type.GetProperty(memberName, bindingFlags);
  166. if ((pi != null) && (pi.PropertyType == typeof(T)))
  167. return (T)pi.GetValue(obj, null);
  168. else
  169. return default(T);
  170. }
  171. #endif
  172. /// <summary>Get the object owner of a field. This method processes
  173. /// the '.' separator to get from the object that owns the compound field
  174. /// to the object that owns the leaf field</summary>
  175. /// <param name="path">The name of the field, which may contain '.' separators</param>
  176. /// <param name="obj">the owner of the compound field</param>
  177. /// <returns>The object owner of the field</returns>
  178. public static object GetParentObject(string path, object obj)
  179. {
  180. var fields = path.Split('.');
  181. if (fields.Length == 1)
  182. return obj;
  183. var info = obj.GetType().GetField(
  184. fields[0], System.Reflection.BindingFlags.Public
  185. | System.Reflection.BindingFlags.NonPublic
  186. | System.Reflection.BindingFlags.Instance);
  187. obj = info.GetValue(obj);
  188. return GetParentObject(string.Join(".", fields, 1, fields.Length - 1), obj);
  189. }
  190. /// <summary>Returns a string path from an expression - mostly used to retrieve serialized properties
  191. /// without hardcoding the field path. Safer, and allows for proper refactoring.</summary>
  192. /// <typeparam name="TType">Magic expression</typeparam>
  193. /// <typeparam name="TValue">Magic expression</typeparam>
  194. /// <param name="expr">Magic expression</param>
  195. /// <returns>The string version of the field path</returns>
  196. public static string GetFieldPath<TType, TValue>(Expression<Func<TType, TValue>> expr)
  197. {
  198. MemberExpression me;
  199. switch (expr.Body.NodeType)
  200. {
  201. case ExpressionType.MemberAccess:
  202. me = expr.Body as MemberExpression;
  203. break;
  204. default:
  205. throw new InvalidOperationException();
  206. }
  207. var members = new List<string>();
  208. while (me != null)
  209. {
  210. members.Add(me.Member.Name);
  211. me = me.Expression as MemberExpression;
  212. }
  213. var sb = new StringBuilder();
  214. for (int i = members.Count - 1; i >= 0; i--)
  215. {
  216. sb.Append(members[i]);
  217. if (i > 0) sb.Append('.');
  218. }
  219. return sb.ToString();
  220. }
  221. }
  222. }