// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved. // This code can only be used under the standard Unity Asset Store End User License Agreement // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms using System; using System.Collections.Generic; using System.Linq; using System.Reflection; // ReSharper disable MemberCanBePrivate.Global // ReSharper disable UnusedMember.Global namespace Doozy.Runtime.Common.Utils { /// Utility class with optimized methods for reflection inside the Doozy assembly public static class ReflectionUtils { private static Assembly[] s_domainAssemblies; /// Current Domain assemblies (cached on first call) public static IEnumerable domainAssemblies => s_domainAssemblies ??= AppDomain.CurrentDomain.GetAssemblies(); private static Assembly s_doozyEditorAssembly; /// Doozy.Editor assembly (only for editor use) (cached on first call) public static Assembly doozyEditorAssembly { get { if (s_doozyEditorAssembly != null) return s_doozyEditorAssembly; foreach (Assembly assembly in domainAssemblies) { if (!assembly.DefinedTypes.Any(typeInfo => typeInfo.Namespace != null && typeInfo.Namespace.Contains("Doozy.Editor."))) continue; s_doozyEditorAssembly = assembly; return s_doozyEditorAssembly; } return s_doozyEditorAssembly; } } private static Assembly s_doozyRuntimeAssembly; /// Doozy.Runtime assembly (for runtime use) (cached on first call) public static Assembly doozyRuntimeAssembly => s_doozyRuntimeAssembly ??= Assembly.GetAssembly(typeof(ReflectionUtils)); private static IEnumerable s_doozyRuntimeTypes; /// Enumeration of all the runtime types inside the Doozy.Runtime assembly (cached on first call) public static IEnumerable doozyRuntimeTypes => s_doozyRuntimeTypes ??= doozyRuntimeAssembly.GetTypes(); /// /// Get all derived types for the givenBase type from the types collection /// /// Collection of types /// Base Type public static IEnumerable GetDerivedTypes(IEnumerable types, Type baseType) { var list = new List(); foreach (Type typeInAssembly in types) { if (typeInAssembly.BaseType != baseType) continue; if (typeInAssembly.IsAbstract) continue; list.Add(typeInAssembly); } return list; } /// /// Get all types that implement the given interface /// /// Type of Interface public static IEnumerable GetTypesThatImplementInterface(Assembly fromAssembly) => fromAssembly .GetTypes() .Where(p => typeof(T).IsAssignableFrom(p) && !p.IsInterface); /// /// Get all types that implement the given interface /// /// Type of Interface public static IEnumerable GetTypesThatImplementInterface() => domainAssemblies .SelectMany(s => s.GetTypes()) .Where(p => typeof(T).IsAssignableFrom(p) && !p.IsInterface); /// /// Get all de derived types for the given baseType and search in the given assembly /// /// Assembly to search for derived types /// Base Type public static IEnumerable GetDerivedTypes(Assembly assembly, Type baseType) => GetDerivedTypes(assembly.GetTypes(), baseType); /// /// Get all the derived types for the given type and search in the same assembly as the Base Type /// /// Base Type public static IEnumerable GetDerivedTypes(Type baseType) => GetDerivedTypes(Assembly.GetAssembly(baseType), baseType); public static IEnumerable GetAttributeReferences(IEnumerable types) where T : Attribute { var list = new List(); foreach (Type type in types) { MemberInfo[] members = type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (MemberInfo member in members) { T attribute = member.GetCustomAttribute(); if (attribute == null) continue; list.Add(attribute); } } return list; } private static bool GetAttribute(IEnumerable attributes, out T attributeOut) where T : Attribute { foreach (object attribute in attributes) { if (attribute.GetType() != typeof(T)) { continue; } attributeOut = attribute as T; return true; } attributeOut = null; return false; } public static bool GetAttribute(Type classType, out T attributeOut) where T : Attribute { object[] attributes = classType.GetCustomAttributes(typeof(T), false); return GetAttribute(attributes, out attributeOut); } public static bool GetAttribute(Type classType, string fieldName, out T attributeOut) where T : Attribute { object[] attributes = classType.GetField(fieldName).GetCustomAttributes(typeof(T), false); return GetAttribute(attributes, out attributeOut); } public static bool HasAttribute(IEnumerable attributes) where T : Attribute => attributes.Any(t => t.GetType() == typeof(T)); /// Returns true if this can be casted to the given type public static bool IsCastableTo(this Type from, Type to) { if (to.IsAssignableFrom(from)) return true; IEnumerable methods = from.GetMethods(BindingFlags.Public | BindingFlags.Static) .Where( m => m.ReturnType == to && (m.Name == "op_Implicit" || m.Name == "op_Explicit") ); return methods.Any(); } /// Return a pretty field type name public static string PrettyName(this Type type) { if (type == null) return "null"; if (type == typeof(object)) return "object"; if (type == typeof(float)) return "float"; if (type == typeof(int)) return "int"; if (type == typeof(long)) return "long"; if (type == typeof(double)) return "double"; if (type == typeof(string)) return "string"; if (type == typeof(bool)) return "bool"; if (type.IsGenericType) { string s = ""; Type genericType = type.GetGenericTypeDefinition(); s = genericType == typeof(List<>) ? "List" : type.GetGenericTypeDefinition().ToString(); Type[] types = type.GetGenericArguments(); string[] stringTypes = new string[types.Length]; for (int i = 0; i < types.Length; i++) stringTypes[i] = types[i].PrettyName(); return s + "<" + string.Join(", ", stringTypes) + ">"; } if (!type.IsArray) return type.ToString(); { string rank = ""; for (int i = 1; i < type.GetArrayRank(); i++) rank += ","; Type elementType = type.GetElementType(); if (elementType is { IsArray: false }) return elementType.PrettyName() + "[" + rank + "]"; { string s = elementType.PrettyName(); int i = s.IndexOf('['); return s.Substring(0, i) + "[" + rank + "]" + s.Substring(i); } } } } }