// ===================================================================== // Copyright 2013-2017 Fluffy Underware // All rights reserved // // http://www.fluffyunderware.com // ===================================================================== using UnityEngine; using System.Collections; using System; using System.Reflection; using System.Collections.Generic; using UnityEngine.Events; #if UNITY_EDITOR using UnityEditor; #endif using System.Text; using System.IO; using System.Linq; using Object = UnityEngine.Object; namespace FluffyUnderware.DevTools.Extensions { public static class ObjectExt { /// /// Calls the proper destruction method based on whether we are in Edit mode or not /// Will show a display dialogue if attempting to destroy a GameObject part of a prefab instance /// /// The object to destroy [Obsolete("Use the other overload of this method")] public static bool Destroy(this UnityEngine.Object @object) { return Destroy(@object, true, true); } /// /// Calls the proper destruction method based on whether we are in Edit mode or not /// Will show a display dialogue if attempting to destroy a GameObject part of a prefab instance /// /// The object to destroy /// Should the object destruction be undoable /// Should this method check that the object destruction is valid, i.e. not deleting a game object that is part of a prefab instance public static bool Destroy(this UnityEngine.Object @object, bool isUndoable, bool doPrefabCheck) { bool wasDestroyed; bool isInEditMode = DTUtility.IsInEditMode; if (isInEditMode) { #if UNITY_EDITOR string errorMessage = string.Empty; bool needsPrefabModification = doPrefabCheck && DTUtility.DoesPrefabStatusAllowDeletion(@object, out errorMessage) == false; if (needsPrefabModification) { EditorUtility.DisplayDialog($"Cannot delete Game Object '{@object.name}'", errorMessage, "Ok"); DTLog.LogError("[Curvy] Invalid operation:" + errorMessage.Replace("\n", String.Empty)); wasDestroyed = false; } else { if (isUndoable) Undo.DestroyObjectImmediate(@object); else Object.DestroyImmediate(@object); wasDestroyed = true; } #else wasDestroyed = false; #endif } else { if (@object is Component) Object.DestroyImmediate(@object); else Object.Destroy(@object); wasDestroyed = true; } return wasDestroyed; } public static string ToDumpString(this object o) { return new DTObjectDump(o).ToString(); } } public static class FloatExt { /// /// Return true if v is between 0 and 1 inclusive /// public static bool IsBetween0And1(this float v) { return v >= 0 && v <= 1; } /// /// Return true if v is between a and b inclusive /// public static bool IsBetween(this float v, float a, float b) { return v >= a && v <= b || v >= b && v <= a; } } public static class Vector2Ext { public static Vector2 Snap(this Vector2 v, float snapX, float snapY = -1) { if (snapY == -1) snapY = snapX; return (new Vector2(v.x - v.x % snapX, v.y - v.y % snapY)); } public static float AngleSigned(this Vector2 a, Vector2 b) { float sign = Mathf.Sign(a.x * b.y - a.y * b.x); return Vector2.Angle(a, b) * sign; } public static Vector2 LeftNormal(this Vector2 v) { return new Vector2(-v.y, v.x); } public static Vector2 RightNormal(this Vector2 v) { return new Vector2(v.y, -v.x); } public static Vector2 Rotate(this Vector2 v, float degree) { float rad = degree * Mathf.Deg2Rad; float c = Mathf.Cos(rad); float s = Mathf.Sin(rad); return new Vector2(c * v.x - s * v.y, s * v.x + c * v.y); } public static Vector2 ToVector3(this Vector2 v) { return new Vector3(v.x, v.y, 0); } } public static class Vector3Ext { public static float AngleSigned(this Vector3 a, Vector3 b, Vector3 normal) { return Mathf.Atan2(Vector3.Dot(normal, Vector3.Cross(a, b)), Vector3.Dot(a, b)) * Mathf.Rad2Deg; } public static Vector3 RotateAround(this Vector3 point, Vector3 origin, Quaternion rotation) { Vector3 dir = point - origin; dir = rotation * dir; return origin + dir; } public static Vector2 ToVector2(this Vector3 v) { Vector2 result; result.x = v.x; result.y = v.y; return result; } /// /// Return true if v1 and v2 are equal, or different but very close. /// /// /// /// public static bool Approximately(this Vector3 v1, Vector3 v2) { Vector3 v1MinusV2 = v1; v1MinusV2.x -= v2.x; v1MinusV2.y -= v2.y; v1MinusV2.z -= v2.z; if (Vector3.SqrMagnitude(v1MinusV2) < 0.000001f) return true; if (Mathf.Approximately(v1.x, v2.x) && Mathf.Approximately(v1.y, v2.y) && Mathf.Approximately(v1.z, v2.z)) { #if CURVY_SANITY_CHECKS_PRIVATE Debug.LogWarning("Approximately went in the third branch"); #endif return true; } return false; } /// /// The opposite of /// /// /// /// public static bool NotApproximately(this Vector3 v1, Vector3 v2) { return Approximately(v1, v2) == false; } } /// /// Extension methods for quaternions /// static public class QuaternionExt { /// /// Two quaternions can represent different rotations that lead to the same final orientation (one rotating around Axis with Angle, the other around -Axis with 2Pi-Angle). In this case, the quaternion == operator will return false. This method will return true. /// /// /// /// public static bool SameOrientation(this Quaternion q1, Quaternion q2) { return Math.Abs((double)Quaternion.Dot(q1, q2)) > 0.999998986721039; } /// /// Two quaternions can represent different rotations that lead to the same final orientation (one rotating around Axis with Angle, the other around -Axis with 2Pi-Angle). In this case, the quaternion != operator will return true. This method will return false. /// /// /// /// public static bool DifferentOrientation(this Quaternion q1, Quaternion q2) { return Math.Abs((double)Quaternion.Dot(q1, q2)) <= 0.999998986721039; } } public static class GameObjectExt { /// /// Duplicates a GameObject /// /// a component being part of the source GameObject /// the component from the cloned GameObject public static GameObject DuplicateGameObject(this GameObject source, Transform newParent, bool keepPrefabReference = false) { if (!source) return null; GameObject newGO; #if UNITY_EDITOR UnityEngine.Object prefabRoot = PrefabUtility.GetCorrespondingObjectFromSource(source.gameObject); if (prefabRoot != null && keepPrefabReference) newGO = PrefabUtility.InstantiatePrefab(prefabRoot) as GameObject; else #endif newGO = Object.Instantiate(source.gameObject) as GameObject; if (newGO) newGO.transform.parent = newParent; return newGO; } public static void StripComponents(this GameObject go, params Type[] toKeep) { List keep = new List(toKeep); keep.Add(typeof(Transform)); keep.Add(typeof(RectTransform)); Component[] cmps = go.GetComponents(); for (int i = 0; i < cmps.Length; i++) if (!keep.Contains(cmps[i].GetType())) cmps[i].Destroy(false, false); } } public static class ComponentExt { public static void StripComponents(this Component c, params Type[] toKeep) { if (toKeep.Length == 0) c.gameObject.StripComponents(c.GetType()); else c.gameObject.StripComponents(toKeep); } public static GameObject AddChildGameObject(this Component c, string name) { GameObject go = new GameObject(name); go.transform.SetParent(c.transform); return go; } public static T AddChildGameObject(this Component c, string name) where T : Component { GameObject go = new GameObject(name); if (go) { go.transform.SetParent(c.transform); return go.AddComponent(); } else return null; } /// /// Duplicates the GameObject of a component, returning the component /// /// a component being part of the source GameObject /// the component from the cloned GameObject public static T DuplicateGameObject(this Component source, Transform newParent, bool keepPrefabConnection = false) where T : Component { if (!source || !source.gameObject) return null; List cmps = new List(source.gameObject.GetComponents()); int sourceIdx = cmps.IndexOf(source); GameObject newGO; #if UNITY_EDITOR UnityEngine.Object prefabRoot = PrefabUtility.GetCorrespondingObjectFromSource(source.gameObject); if (prefabRoot != null && keepPrefabConnection) newGO = PrefabUtility.InstantiatePrefab(prefabRoot) as GameObject; else #endif newGO = Object.Instantiate(source.gameObject); if (newGO) { newGO.transform.SetParent(newParent, false); Component[] newCmps = newGO.GetComponents(); return newCmps[sourceIdx] as T; } else return null; } /// /// Duplicates the GameObject of a component, returning the component /// /// a component being part of the source GameObject /// the component from the cloned GameObject public static Component DuplicateGameObject(this Component source, Transform newParent, bool keepPrefabConnection = false) { if (!source || !source.gameObject || !newParent) return null; List cmps = new List(source.gameObject.GetComponents()); int sourceIdx = cmps.IndexOf(source); GameObject newGO; #if UNITY_EDITOR UnityEngine.Object prefabRoot = PrefabUtility.GetCorrespondingObjectFromSource(source.gameObject); if (prefabRoot != null && keepPrefabConnection) newGO = PrefabUtility.InstantiatePrefab(prefabRoot) as GameObject; else #endif newGO = Object.Instantiate(source.gameObject); if (newGO) { newGO.transform.SetParent(newParent, false); Component[] newCmps = newGO.GetComponents(); return newCmps[sourceIdx]; } else return null; } } public static class ColorExt { public static string ToHtml(this Color c) { Color32 col = c; return string.Format("#{0:X2}{1:X2}{2:X2}{3:X2}", new object[] { col.r, col.g, col.b, col.a }); } } public static class EnumExt { /// /// Checks if at least one of the provided flags is set in variable /// public static bool HasFlag(this Enum variable, params Enum[] flags) { if (flags.Length == 0) throw new ArgumentNullException("flags"); int varInt = Convert.ToInt32(variable); Type T = variable.GetType(); for (int i = 0; i < flags.Length; i++) { if (!Enum.IsDefined(T, flags[i])) { throw new ArgumentException(string.Format( "Enumeration type mismatch. The flag is of type '{0}', was expecting '{1}'.", flags[i].GetType(), T)); } int num = Convert.ToInt32(flags[i]); if ((varInt & num) == num) return true; } return false; } public static bool HasFlag(this T value, T flag) where T : struct { long lValue = Convert.ToInt64(value); long lFlag = Convert.ToInt64(flag); return (lValue & lFlag) != 0; } /// /// Sets a flag /// public static T Set(this Enum value, T append) { return Set(value, append, true); } /// /// Sets a flag /// /// whether to set or unset the value public static T Set(this Enum value, T append, bool OnOff) { if (append == null) throw new ArgumentNullException("append"); Type type = value.GetType(); //return the final value if (OnOff) return (T)Enum.Parse(type, (Convert.ToUInt64(value) | Convert.ToUInt64(append)).ToString()); else return (T)Enum.Parse(type, (Convert.ToUInt64(value) & ~Convert.ToUInt64(append)).ToString()); } } public static class RectExt { public static Rect Set(this Rect rect, Vector2 pos, Vector2 size) { rect.Set(pos.x, pos.y, size.x, size.y); return new Rect(rect); } public static Rect SetBetween(this Rect rect, Vector2 pos, Vector2 pos2) { rect.Set(pos.x, pos.y, pos2.x - pos.x, pos2.y - pos.y); return new Rect(rect); } /// /// Sets x/y /// public static Rect SetPosition(this Rect rect, Vector2 pos) { rect.x = pos.x; rect.y = pos.y; return new Rect(rect); } /// /// Sets x/y /// public static Rect SetPosition(this Rect rect, float x, float y) { rect.x = x; rect.y = y; return new Rect(rect); } /// /// gets width/height as Vector2 /// public static Vector2 GetSize(this Rect rect) { return new Vector2(rect.width, rect.height); } /// /// Sets width/height /// public static Rect SetSize(this Rect rect, Vector2 size) { rect.width = size.x; rect.height = size.y; return new Rect(rect); } /// /// Grow/Shrink a rect /// public static Rect ScaleBy(this Rect rect, int pixel) { return ScaleBy(rect, pixel, pixel); } /// /// Grow/Shrink a rect /// public static Rect ScaleBy(this Rect rect, int x, int y) { rect.x -= (float)x; rect.y -= (float)y; rect.width += (float)x * 2; rect.height += (float)y * 2; return new Rect(rect); } public static Rect ShiftBy(this Rect rect, int x, int y) { rect.x += (float)x; rect.y += (float)y; return new Rect(rect); } public static Rect Include(this Rect rect, Rect other) { Rect r = new Rect(); r.xMin = Mathf.Min(rect.xMin, other.xMin); r.xMax = Mathf.Max(rect.xMax, other.xMax); r.yMin = Mathf.Min(rect.yMin, other.yMin); r.yMax = Mathf.Max(rect.yMax, other.yMax); return r; } } public static class StringExt { /// /// Converts a HTML color endcoded string int a color /// /// html color of type [#]rrggbb[aa] /// a Color public static Color ColorFromHtml(this string hexString) { if (hexString.Length < 9) hexString += "FF"; if (hexString.StartsWith("#") && hexString.Length == 9) { int[] rgba = new int[4]; try { rgba[0] = int.Parse(hexString.Substring(1, 2), System.Globalization.NumberStyles.HexNumber); rgba[1] = int.Parse(hexString.Substring(3, 2), System.Globalization.NumberStyles.HexNumber); rgba[2] = int.Parse(hexString.Substring(5, 2), System.Globalization.NumberStyles.HexNumber); rgba[3] = int.Parse(hexString.Substring(7, 2), System.Globalization.NumberStyles.HexNumber); return new Color(rgba[0] / 255f, rgba[1] / 255f, rgba[2] / 255f, rgba[3] / 255f); } catch (Exception e) { Debug.LogException(e); return Color.white; } } return Color.white; } public static string TrimStart(this string s, string trim, StringComparison compare = StringComparison.CurrentCultureIgnoreCase) { if (!s.StartsWith(trim, compare)) return s; else { return s.Substring(trim.Length); } } public static string TrimEnd(this string s, string trim, StringComparison compare = StringComparison.CurrentCultureIgnoreCase) { if (!s.EndsWith(trim, compare)) return s; else { return s.Substring(0, s.Length - trim.Length); } } } public static class IEnumerableExt { public static void ForEach(this IEnumerable ie, Action action) { foreach (T i in ie) { action(i); } } } public static class ArrayExt { public static T[] SubArray(this T[] data, int index, int length) { length = Mathf.Clamp(length, 0, data.Length - index); T[] result = new T[length]; if (length > 0) Array.Copy(data, index, result, 0, length); return result; } public static T[] RemoveAt(this T[] source, int index) { T[] dest = new T[source.Length - 1]; if (index > 0) Array.Copy(source, 0, dest, 0, index); if (index < source.Length - 1) Array.Copy(source, index + 1, dest, index, source.Length - index - 1); return dest; } public static T[] InsertAt(this T[] source, int index) { T[] dest = new T[source.Length + 1]; index = Mathf.Clamp(index, 0, source.Length - 1); if (index > 0) Array.Copy(source, 0, dest, 0, index); Array.Copy(source, index, dest, index + 1, source.Length - index); return dest; } public static T[] Swap(this T[] source, int index, int with) { index = Mathf.Clamp(index, 0, source.Length - 1); with = Mathf.Clamp(index, 0, source.Length - 1); T tmp = source[index]; source[index] = source[with]; source[with] = tmp; return source; } public static T[] Add(this T[] source, T item) { Array.Resize(ref source, source.Length + 1); source[source.Length - 1] = item; return source; } public static T[] AddRange(this T[] source, T[] items) { Array.Resize(ref source, source.Length + items.Length); Array.Copy(items, 0, source, source.Length - items.Length, items.Length); return source; } public static T[] RemoveDuplicates(this T[] source) { List res = new List(); HashSet hash = new HashSet(); foreach (T p in source) { if (hash.Add(p)) { res.Add(p); } } return res.ToArray(); } public static int IndexOf(this T[] source, T item) { for (int i = 0; i < source.Length; i++) if (source[i].Equals(item)) return i; return -1; } public static T[] Remove(this T[] source, T item) { int idx = source.IndexOf(item); if (idx > -1) return source.RemoveAt(idx); else return source; } } public static class MeshFilterExt { /// /// Returns a shared mesh to work with. If existing, it will be cleared /// public static Mesh PrepareNewShared(this MeshFilter m, string name = "Mesh") { if (m == null) return null; if (m.sharedMesh == null) { Mesh msh = new Mesh(); msh.MarkDynamic(); msh.name = name; m.sharedMesh = msh; } else { m.sharedMesh.Clear(); m.sharedMesh.name = name; m.sharedMesh.subMeshCount = 0; } return m.sharedMesh; } [Obsolete("No more used in Curvy. Will get removed. Copy it if you still need it")] public static void CalculateTangents(this MeshFilter m) { //speed up math by copying the mesh arrays int[] triangles = m.sharedMesh.triangles; Vector3[] vertices = m.sharedMesh.vertices; Vector2[] uv = m.sharedMesh.uv; Vector3[] normals = m.sharedMesh.normals; if (uv.Length == 0) return; //variable definitions int triangleCount = triangles.Length; int vertexCount = vertices.Length; Vector3[] tan1 = new Vector3[vertexCount]; Vector3[] tan2 = new Vector3[vertexCount]; Vector4[] tangents = new Vector4[vertexCount]; for (int a = 0; a < triangleCount; a += 3) { int i1 = triangles[a + 0]; int i2 = triangles[a + 1]; int i3 = triangles[a + 2]; Vector3 v1 = vertices[i1]; Vector3 v2 = vertices[i2]; Vector3 v3 = vertices[i3]; Vector2 w1 = uv[i1]; Vector2 w2 = uv[i2]; Vector2 w3 = uv[i3]; float x1 = v2.x - v1.x; float x2 = v3.x - v1.x; float y1 = v2.y - v1.y; float y2 = v3.y - v1.y; float z1 = v2.z - v1.z; float z2 = v3.z - v1.z; float s1 = w2.x - w1.x; float s2 = w3.x - w1.x; float t1 = w2.y - w1.y; float t2 = w3.y - w1.y; float div = s1 * t2 - s2 * t1; float r = div == 0.0f ? 0.0f : 1.0f / div; float sdirX = (t2 * x1 - t1 * x2) * r; float sdirY = (t2 * y1 - t1 * y2) * r; float sdirZ = (t2 * z1 - t1 * z2) * r; float tdirX = (s1 * x2 - s2 * x1) * r; float tdirY = (s1 * y2 - s2 * y1) * r; float tdirZ = (s1 * z2 - s2 * z1) * r; tan1[i1].x += sdirX; tan1[i1].y += sdirY; tan1[i1].z += sdirZ; tan1[i2].x += sdirX; tan1[i2].y += sdirY; tan1[i2].z += sdirZ; tan1[i3].x += sdirX; tan1[i3].y += sdirY; tan1[i3].z += sdirZ; tan2[i1].x += tdirX; tan2[i1].y += tdirY; tan2[i1].z += tdirZ; tan2[i2].x += tdirX; tan2[i2].y += tdirY; tan2[i2].z += tdirZ; tan2[i3].x += tdirX; tan2[i3].y += tdirY; tan2[i3].z += tdirZ; } for (int a = 0; a < vertexCount; ++a) { Vector3 n = normals[a]; Vector3 t = tan1[a]; Vector3.OrthoNormalize(ref n, ref t); tangents[a].x = t.x; tangents[a].y = t.y; tangents[a].z = t.z; //inlined version of float dotOfCross = (Vector3.Dot(Vector3.Cross(n, t), tan2[a]) < 0.0f) float dotOfCross = ((n.y * t.z - n.z * t.y) * tan2[a].x + (n.z * t.x - n.x * t.z) * tan2[a].y + (n.x * t.y - n.y * t.x) * tan2[a].z); tangents[a].w = (dotOfCross < 0.0f) ? -1.0f : 1.0f; } m.sharedMesh.tangents = tangents; } } public static class TypeExt { /// /// Gets all types loaded in the current domain. Is not compatible with NETFX_CORE since AppDomain is not available in .Net core 1. AppDomain is back in .Net core 2, but unity isn't compatible with it yet. /// #if NETFX_CORE [Obsolete("Is not compatible with NETFX_CORE since AppDomain is not available in .Net core 1. AppDomain is back in .Net core 2, but unity isn't compatible with it yet. Use Type[] GetAllTypes(this Type typeFromAssembly) instead")] #endif #if UNITY_EDITOR [Obsolete("Use TypeCache.GetTypesDerivedFrom(typeof(System.Object)) instead")] #endif public static Type[] GetLoadedTypes() { #if UNITY_EDITOR return TypeCache.GetTypesDerivedFrom(typeof(System.Object)).ToArray(); #elif NETFX_CORE //OPTIM use GetExportedTypes instead of GetTypes? return GetLoadedAssemblies().SelectMany(a => a.DefinedTypes).Select(typeInfo => typeInfo.AsType()).ToArray(); #else IEnumerable loadedAssemblies = GetLoadedAssemblies(); List types = new List(loadedAssemblies.Count() * 100);//An estimation of 100 type per assembly. This is based on no statistical analysis, just a guess. foreach (Assembly assembly in loadedAssemblies) { try { types.AddRange(assembly.GetTypes()); } catch (ReflectionTypeLoadException exception) { for (int index = 0; index < exception.Types.Length; index++) { Type type = exception.Types[index]; if (type != null) types.Add(type); } } } return types.ToArray(); #endif } /// /// Gets all types loaded assemblies in the current domain. Is not compatible with NETFX_CORE since AppDomain is not available in .Net core 1. AppDomain is back in .Net core 2, but unity isn't compatible with it yet. /// #if NETFX_CORE [Obsolete("Is not compatible with NETFX_CORE since AppDomain is not available in .Net core 1. AppDomain is back in .Net core 2, but unity isn't compatible with it yet.")] #endif static public IEnumerable GetLoadedAssemblies() { //TODO find a way to make this work when NETFX_CORE is true #if NETFX_CORE throw new NotImplementedException("Is not compatible with NETFX_CORE since AppDomain is not available in .Net core 1. AppDomain is back in .Net core 2, but unity isn't compatible with it yet."); #else //OPTIM use .Where(a => a.GlobalAssemblyCache == false)? return AppDomain.CurrentDomain.GetAssemblies(); #endif } /// /// Gets all Types T that have an attribute U /// public static Dictionary GetAllTypesWithAttribute(this Type type) { Dictionary res = new Dictionary(); IEnumerable loadedTypes; #if UNITY_EDITOR loadedTypes = TypeCache.GetTypesDerivedFrom(type); #else #if NETFX_CORE loadedTypes = type.GetAllTypes(); #else loadedTypes = GetLoadedTypes(); #endif #endif foreach (Type t in loadedTypes) { #if UNITY_EDITOR == false #if NETFX_CORE if (t.GetTypeInfo().IsSubclassOf(type)) #else if (t.IsSubclassOf(type)) #endif #endif { #if NETFX_CORE object[] attribs = (object[])t.GetTypeInfo().GetCustomAttributes(typeof(U), false); #else object[] attribs = t.GetCustomAttributes(typeof(U), false); #endif if (attribs.Length > 0) { res.Add((U)attribs[0], t); } } } return res; } /// /// Gets all fields of a type that have a certain attribute /// public static List GetFieldsWithAttribute(this Type type, bool includeInherited = false, bool includePrivate = false) where T : Attribute { FieldInfo[] flds = type.GetAllFields(includeInherited, includePrivate); List res = new List(); foreach (FieldInfo fi in flds) { if (fi.GetCustomAttribute() != null) res.Add(fi); } return res; } /// /// Gets a custom attribute from a type (Crossplatform) /// public static T GetCustomAttribute(this Type type) where T : Attribute { #if NETFX_CORE object[] at = (object[])type.GetTypeInfo().GetCustomAttributes(typeof(T), true); #else object[] at = (object[])type.GetCustomAttributes(typeof(T), true); #endif return (at.Length > 0) ? (T)at[0] : null; } /// /// Finds a Method (Crossplatform) /// /// type containing the method /// Name of method /// Whether methods of base classes should be returned as well /// Whether private methods should be returned as well public static MethodInfo MethodByName(this Type type, string name, bool includeInherited = false, bool includePrivate = false) { #if NETFX_CORE MethodInfo res=type.GetRuntimeMethods().Where(mi => (mi.IsPublic || includePrivate) && mi.DeclaringType==type && mi.Name==name).FirstOrDefault(); if (res==null && includeInherited) { type=type.GetTypeInfo().BaseType; while (res==null && type!=typeof(object)) { res=type.GetRuntimeMethods().Where(mi => (mi.IsPublic || includePrivate) && mi.DeclaringType==type && mi.Name==name).FirstOrDefault(); type=type.GetTypeInfo().BaseType; } } return res; #else BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; if (includePrivate) flags = flags | BindingFlags.NonPublic; if (includeInherited) return type.GetMethodIncludingBaseClasses(name, flags); else return type.GetMethod(name, flags); #endif } /// /// Finds a Field (Crossplatform) /// /// type containing the field /// Name of field /// Whether fields of base classes should be returned as well /// Whether private fields should be returned as well public static FieldInfo FieldByName(this Type type, string name, bool includeInherited = false, bool includePrivate = false) { #if NETFX_CORE FieldInfo res=type.GetRuntimeFields().Where(fi => (fi.IsPublic || includePrivate) && fi.DeclaringType==type && fi.Name==name).FirstOrDefault(); if (res==null && includeInherited) { type=type.GetTypeInfo().BaseType; while (res==null && type!=typeof(object)) { res=type.GetRuntimeFields().Where(fi => (fi.IsPublic || includePrivate) && fi.DeclaringType==type && fi.Name==name).FirstOrDefault(); type=type.GetTypeInfo().BaseType; } } return res; #else BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; if (includePrivate) flags = flags | BindingFlags.NonPublic; if (includeInherited) return type.GetFieldIncludingBaseClasses(name, flags); else return type.GetField(name, flags); #endif } /// /// Finds a Property (Crossplatform) /// /// type containing the property /// Name of property /// Whether properties of base classes should be returned as well /// Whether private properties should be returned as well public static PropertyInfo PropertyByName(this Type type, string name, bool includeInherited = false, bool includePrivate = false) { #if NETFX_CORE PropertyInfo res=type.GetRuntimeProperties().Where(pi => ((pi.GetMethod!=null && pi.GetMethod.IsPublic) || includePrivate) && pi.DeclaringType==type && pi.Name==name).FirstOrDefault(); if (res==null && includeInherited) { type=type.GetTypeInfo().BaseType; while (res==null && type!=typeof(object)) { res=type.GetRuntimeProperties().Where(pi => ((pi.GetMethod!=null && pi.GetMethod.IsPublic) || includePrivate) && pi.DeclaringType==type && pi.Name==name).FirstOrDefault(); type=type.GetTypeInfo().BaseType; } } return res; #else BindingFlags flags = BindingFlags.Public | BindingFlags.Instance; if (includePrivate) flags = flags | BindingFlags.NonPublic; if (includeInherited) return type.GetPropertyIncludingBaseClasses(name, flags); else return type.GetProperty(name, flags); #endif } /// /// Gets all fields (Crossplatform) /// /// type to reflect /// Whether fields of base classes should be returned as well /// Whether private fields should be returned as well public static FieldInfo[] GetAllFields(this Type type, bool includeInherited = false, bool includePrivate = false) { #if NETFX_CORE List res=type.GetRuntimeFields().Where(fi => (fi.IsPublic || includePrivate) && fi.DeclaringType==type).ToList(); if (includeInherited) { type=type.GetTypeInfo().BaseType; while (type!=typeof(object)) { res.AddRange(type.GetRuntimeFields().Where(fi => (fi.IsPublic || includePrivate) && fi.DeclaringType==type).ToArray()); type=type.GetTypeInfo().BaseType; } } return res.ToArray(); #else BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; if (includePrivate) flags = flags | BindingFlags.NonPublic; if (includeInherited) { Type currentType = type; List res = new List(); while (currentType != typeof(object)) { res.AddRange(currentType.GetFields(flags)); currentType = currentType.BaseType; } return res.ToArray(); } else return type.GetFields(flags); #endif } /// /// Gets all properties (Crossplatform) /// /// type to reflect /// Whether properties of base classes should be returned as well /// Whether private properties should be returned as well public static PropertyInfo[] GetAllProperties(this Type type, bool includeInherited = false, bool includePrivate = false) { #if NETFX_CORE List res=type.GetRuntimeProperties().Where(pi => ((pi.GetMethod!=null && pi.GetMethod.IsPublic) || includePrivate) && pi.DeclaringType==type).ToList(); if (includeInherited) { type=type.GetTypeInfo().BaseType; while (type!=typeof(object)) { res.AddRange(type.GetRuntimeProperties().Where(pi => ((pi.GetMethod!=null && pi.GetMethod.IsPublic) || includePrivate) && pi.DeclaringType==type).ToArray()); type=type.GetTypeInfo().BaseType; } } return res.ToArray(); #else BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly; if (includePrivate) flags = flags | BindingFlags.NonPublic; if (includeInherited) { Type currentType = type; List res = new List(); while (currentType != typeof(object)) { res.AddRange(currentType.GetProperties(flags)); currentType = currentType.BaseType; } return res.ToArray(); } else return type.GetProperties(flags); #endif } /// /// Whether the type is a framework type, i.e. a primitive, string or DateTime (Crossplatform) /// public static bool IsFrameworkType(this Type type) { #if NETFX_CORE return type.GetTypeInfo().IsPrimitive || type.Equals(typeof(string)) || type.Equals(typeof(DateTime)); #else return type.IsPrimitive || type.Equals(typeof(string)) || type.Equals(typeof(DateTime)); #endif } public static bool IsArrayOrList(this Type type) { #if NETFX_CORE return (type.IsArray || (type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))); #else return (type.IsArray || (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))); #endif } #if !NETFX_CORE // not NETFX_CORE compliant public static Type GetEnumerableType(this Type t) { Type ienum = FindIEnumerable(t); if (ienum == null) return t; return ienum.GetGenericArguments()[0]; } // not NETFX_CORE compliant static Type FindIEnumerable(Type seqType) { if (seqType == null || seqType == typeof(string)) return null; if (seqType.IsArray) return typeof(IEnumerable<>).MakeGenericType(seqType.GetElementType()); if (seqType.IsGenericType) { foreach (Type arg in seqType.GetGenericArguments()) { Type ienum = typeof(IEnumerable<>).MakeGenericType(arg); if (ienum.IsAssignableFrom(seqType)) { return ienum; } } } Type[] ifaces = seqType.GetInterfaces(); if (ifaces != null && ifaces.Length > 0) { foreach (Type iface in ifaces) { Type ienum = FindIEnumerable(iface); if (ienum != null) return ienum; } } if (seqType.BaseType != null && seqType.BaseType != typeof(object)) { return FindIEnumerable(seqType.BaseType); } return null; } // not NETFX_CORE compliant static MethodInfo GetMethodIncludingBaseClasses(this Type type, string name, BindingFlags bindingFlags) { // If this class doesn't have a base, don't waste any time MethodInfo mi = type.GetMethod(name, bindingFlags); if (type.BaseType == typeof(object)) { return mi; } else { // Otherwise, collect all types up to the furthest base class Type currentType = type; while (currentType != typeof(object)) { mi = currentType.GetMethod(name, bindingFlags); if (mi != null) return mi; currentType = currentType.BaseType; } return null; } } // not NETFX_CORE compliant static FieldInfo GetFieldIncludingBaseClasses(this Type type, string name, BindingFlags bindingFlags) { FieldInfo fieldInfo = type.GetField(name, bindingFlags); // If this class doesn't have a base, don't waste any time if (type.BaseType == typeof(object)) { return fieldInfo; } else { // Otherwise, collect all types up to the furthest base class Type currentType = type; while (currentType != typeof(object)) { fieldInfo = currentType.GetField(name, bindingFlags); if (fieldInfo != null) return fieldInfo; currentType = currentType.BaseType; } return null; } } // not NETFX_CORE compliant static PropertyInfo GetPropertyIncludingBaseClasses(this Type type, string name, BindingFlags bindingFlags) { PropertyInfo propertyInfo = type.GetProperty(name, bindingFlags); // If this class doesn't have a base, don't waste any time if (type.BaseType == typeof(object)) { return propertyInfo; } else { // Otherwise, collect all types up to the furthest base class Type currentType = type; while (currentType != typeof(object)) { propertyInfo = currentType.GetProperty(name, bindingFlags); if (propertyInfo != null) return propertyInfo; currentType = currentType.BaseType; } return null; } } #endif public static bool Matches(this Type type, params Type[] types) { #if NETFX_CORE var ti = type.GetTypeInfo(); #endif foreach (Type t in types) #if NETFX_CORE if (type == t || ti.IsAssignableFrom(t.GetTypeInfo())) #else if (type == t || type.IsAssignableFrom(t)) #endif return true; return false; } } public static class FieldInfoExt { #if !NETFX_CORE /// /// Gets a custom attribute (CrossPlatform) /// public static T GetCustomAttribute(this FieldInfo field) where T : Attribute { object[] at = field.GetCustomAttributes(typeof(T), true); return (at.Length > 0) ? (T)at[0] : null; } #endif } }