| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Text;
- namespace Cinemachine.Utility
- {
- /// <summary>An ad-hoc collection of helpers for reflection, used by Cinemachine
- /// or its editor tools in various places</summary>
- [DocumentationSorting(DocumentationSortingAttribute.Level.Undoc)]
- public static class ReflectionHelpers
- {
- /// <summary>Copy the fields from one object to another</summary>
- /// <param name="src">The source object to copy from</param>
- /// <param name="dst">The destination object to copy to</param>
- /// <param name="bindingAttr">The mask to filter the attributes.
- /// Only those fields that get caught in the filter will be copied</param>
- public static void CopyFields(
- System.Object src, System.Object dst,
- System.Reflection.BindingFlags bindingAttr
- = System.Reflection.BindingFlags.Public
- | System.Reflection.BindingFlags.NonPublic
- | System.Reflection.BindingFlags.Instance)
- {
- if (src != null && dst != null)
- {
- Type type = src.GetType();
- FieldInfo[] fields = type.GetFields(bindingAttr);
- for (int i = 0; i < fields.Length; ++i)
- if (!fields[i].IsStatic)
- fields[i].SetValue(dst, fields[i].GetValue(src));
- }
- }
- /// <summary>Search the assembly for all types that match a predicate</summary>
- /// <param name="assembly">The assembly to search</param>
- /// <param name="predicate">The type to look for</param>
- /// <returns>A list of types found in the assembly that inherit from the predicate</returns>
- public static IEnumerable<Type> GetTypesInAssembly(
- Assembly assembly, Predicate<Type> predicate)
- {
- if (assembly == null)
- return null;
- Type[] types = new Type[0];
- try
- {
- types = assembly.GetTypes();
- }
- catch (Exception)
- {
- // Can't load the types in this assembly
- }
- types = (from t in types
- where t != null && predicate(t)
- select t).ToArray();
- return types;
- }
- /// <summary>Get a type from a name</summary>
- /// <param name="typeName">The name of the type to search for</param>
- /// <returns>The type matching the name, or null if not found</returns>
- public static Type GetTypeInAllDependentAssemblies(string typeName)
- {
- foreach (Type type in GetTypesInAllDependentAssemblies(t => t.Name == typeName))
- return type;
- return null;
- }
- /// <summary>Search all assemblies for all types that match a predicate</summary>
- /// <param name="predicate">The type to look for</param>
- /// <returns>A list of types found in the assembly that inherit from the predicate</returns>
- public static IEnumerable<Type> GetTypesInAllDependentAssemblies(Predicate<Type> predicate)
- {
- List<Type> foundTypes = new List<Type>(100);
- Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
- string definedIn = typeof(CinemachineComponentBase).Assembly.GetName().Name;
- foreach (Assembly assembly in assemblies)
- {
- // Note that we have to call GetName().Name. Just GetName() will not work.
- if ((!assembly.GlobalAssemblyCache)
- && ((assembly.GetName().Name == definedIn)
- || assembly.GetReferencedAssemblies().Any(a => a.Name == definedIn)))
- try
- {
- foreach (Type foundType in GetTypesInAssembly(assembly, predicate))
- foundTypes.Add(foundType);
- }
- catch (Exception) {} // Just skip uncooperative assemblies
- }
- return foundTypes;
- }
- #if false
- /// <summary>call GetTypesInAssembly() for all assemblies that match a predicate</summary>
- /// <param name="assemblyPredicate">Which assemblies to search</param>
- /// <param name="predicate">What type to look for</param>
- public static IEnumerable<Type> GetTypesInLoadedAssemblies(
- Predicate<Assembly> assemblyPredicate, Predicate<Type> predicate)
- {
- Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
- assemblies = assemblies.Where((Assembly assembly)
- => { return assemblyPredicate(assembly); }).OrderBy((Assembly ass)
- => { return ass.FullName; }).ToArray();
- List<Type> foundTypes = new List<Type>(100);
- foreach (Assembly assembly in assemblies)
- {
- foreach (Type foundType in GetTypesInAssembly(assembly, predicate))
- foundTypes.Add(foundType);
- }
- return foundTypes;
- }
- /// <summary>Is a type defined and visible</summary>
- /// <param name="fullname">Fullly-qualified type name</param>
- /// <returns>true if the type exists</returns>
- public static bool TypeIsDefined(string fullname)
- {
- Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
- foreach (Assembly assembly in assemblies)
- {
- try
- {
- foreach (var type in assembly.GetTypes())
- if (type.FullName == fullname)
- return true;
- }
- catch (System.Exception) {} // Just skip uncooperative assemblies
- }
- return false;
- }
- #endif
- /// <summary>Cheater extension to access internal field of an object</summary>
- /// <typeparam name="T">The field type</typeparam>
- /// <param name="type">The type of the field</param>
- /// <param name="obj">The object to access</param>
- /// <param name="memberName">The string name of the field to access</param>
- /// <returns>The value of the field in the objects</returns>
- public static T AccessInternalField<T>(this Type type, object obj, string memberName)
- {
- if (string.IsNullOrEmpty(memberName) || (type == null))
- return default(T);
- System.Reflection.BindingFlags bindingFlags = System.Reflection.BindingFlags.NonPublic;
- if (obj != null)
- bindingFlags |= System.Reflection.BindingFlags.Instance;
- else
- bindingFlags |= System.Reflection.BindingFlags.Static;
- FieldInfo field = type.GetField(memberName, bindingFlags);
- if ((field != null) && (field.FieldType == typeof(T)))
- return (T)field.GetValue(obj);
- else
- return default(T);
- }
- #if false
- /// <summary>Cheater extension to access internal property of an object</summary>
- /// <typeparam name="T">The field type</typeparam>
- /// <param name="type">The type of the field</param>
- /// <param name="obj">The object to access</param>
- /// <param name="memberName">The string name of the field to access</param>
- /// <returns>The value of the field in the objects</returns>
- public static T AccessInternalProperty<T>(this Type type, object obj, string memberName)
- {
- if (string.IsNullOrEmpty(memberName) || (type == null))
- return default(T);
- System.Reflection.BindingFlags bindingFlags = System.Reflection.BindingFlags.NonPublic;
- if (obj != null)
- bindingFlags |= System.Reflection.BindingFlags.Instance;
- else
- bindingFlags |= System.Reflection.BindingFlags.Static;
- PropertyInfo pi = type.GetProperty(memberName, bindingFlags);
- if ((pi != null) && (pi.PropertyType == typeof(T)))
- return (T)pi.GetValue(obj, null);
- else
- return default(T);
- }
- #endif
- /// <summary>Get the object owner of a field. This method processes
- /// the '.' separator to get from the object that owns the compound field
- /// to the object that owns the leaf field</summary>
- /// <param name="path">The name of the field, which may contain '.' separators</param>
- /// <param name="obj">the owner of the compound field</param>
- /// <returns>The object owner of the field</returns>
- public static object GetParentObject(string path, object obj)
- {
- var fields = path.Split('.');
- if (fields.Length == 1)
- return obj;
- var info = obj.GetType().GetField(
- fields[0], System.Reflection.BindingFlags.Public
- | System.Reflection.BindingFlags.NonPublic
- | System.Reflection.BindingFlags.Instance);
- obj = info.GetValue(obj);
- return GetParentObject(string.Join(".", fields, 1, fields.Length - 1), obj);
- }
- /// <summary>Returns a string path from an expression - mostly used to retrieve serialized properties
- /// without hardcoding the field path. Safer, and allows for proper refactoring.</summary>
- /// <typeparam name="TType">Magic expression</typeparam>
- /// <typeparam name="TValue">Magic expression</typeparam>
- /// <param name="expr">Magic expression</param>
- /// <returns>The string version of the field path</returns>
- public static string GetFieldPath<TType, TValue>(Expression<Func<TType, TValue>> expr)
- {
- MemberExpression me;
- switch (expr.Body.NodeType)
- {
- case ExpressionType.MemberAccess:
- me = expr.Body as MemberExpression;
- break;
- default:
- throw new InvalidOperationException();
- }
- var members = new List<string>();
- while (me != null)
- {
- members.Add(me.Member.Name);
- me = me.Expression as MemberExpression;
- }
- var sb = new StringBuilder();
- for (int i = members.Count - 1; i >= 0; i--)
- {
- sb.Append(members[i]);
- if (i > 0) sb.Append('.');
- }
- return sb.ToString();
- }
- }
- }
|