1303 lines
46 KiB
C#
1303 lines
46 KiB
C#
// =====================================================================
|
|
// 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
|
|
{
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <param name="object">The object to destroy</param>
|
|
[Obsolete("Use the other overload of this method")]
|
|
public static bool Destroy(this UnityEngine.Object @object)
|
|
{
|
|
return Destroy(@object, true, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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
|
|
/// </summary>
|
|
/// <param name="object">The object to destroy</param>
|
|
/// <param name="isUndoable">Should the object destruction be undoable</param>
|
|
/// <param name="doPrefabCheck">Should this method check that the object destruction is valid, i.e. not deleting a game object that is part of a prefab instance</param>
|
|
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
|
|
{
|
|
/// <summary>
|
|
/// Return true if v is between 0 and 1 inclusive
|
|
/// </summary>
|
|
public static bool IsBetween0And1(this float v)
|
|
{
|
|
return v >= 0 && v <= 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return true if v is between a and b inclusive
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Return true if v1 and v2 are equal, or different but very close.
|
|
/// </summary>
|
|
/// <param name="v1"></param>
|
|
/// <param name="v2"></param>
|
|
/// <returns></returns>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The opposite of <see cref="Approximately"/>
|
|
/// </summary>
|
|
/// <param name="v1"></param>
|
|
/// <param name="v2"></param>
|
|
/// <returns></returns>
|
|
public static bool NotApproximately(this Vector3 v1, Vector3 v2)
|
|
{
|
|
return Approximately(v1, v2) == false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Extension methods for quaternions
|
|
/// </summary>
|
|
static public class QuaternionExt
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="q1"></param>
|
|
/// <param name="q2"></param>
|
|
/// <returns></returns>
|
|
public static bool SameOrientation(this Quaternion q1, Quaternion q2)
|
|
{
|
|
return Math.Abs((double)Quaternion.Dot(q1, q2)) > 0.999998986721039;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
/// <param name="q1"></param>
|
|
/// <param name="q2"></param>
|
|
/// <returns></returns>
|
|
public static bool DifferentOrientation(this Quaternion q1, Quaternion q2)
|
|
{
|
|
return Math.Abs((double)Quaternion.Dot(q1, q2)) <= 0.999998986721039;
|
|
}
|
|
}
|
|
|
|
public static class GameObjectExt
|
|
{
|
|
/// <summary>
|
|
/// Duplicates a GameObject
|
|
/// </summary>
|
|
/// <param name="source">a component being part of the source GameObject</param>
|
|
/// <returns>the component from the cloned GameObject</returns>
|
|
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<Type> keep = new List<Type>(toKeep);
|
|
keep.Add(typeof(Transform));
|
|
keep.Add(typeof(RectTransform));
|
|
Component[] cmps = go.GetComponents<Component>();
|
|
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<T>(this Component c, string name) where T : Component
|
|
{
|
|
GameObject go = new GameObject(name);
|
|
if (go)
|
|
{
|
|
go.transform.SetParent(c.transform);
|
|
return go.AddComponent<T>();
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Duplicates the GameObject of a component, returning the component
|
|
/// </summary>
|
|
/// <param name="source">a component being part of the source GameObject</param>
|
|
/// <returns>the component from the cloned GameObject</returns>
|
|
public static T DuplicateGameObject<T>(this Component source, Transform newParent, bool keepPrefabConnection = false) where T : Component
|
|
{
|
|
if (!source || !source.gameObject)
|
|
return null;
|
|
|
|
List<Component> cmps = new List<Component>(source.gameObject.GetComponents<Component>());
|
|
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<Component>();
|
|
return newCmps[sourceIdx] as T;
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Duplicates the GameObject of a component, returning the component
|
|
/// </summary>
|
|
/// <param name="source">a component being part of the source GameObject</param>
|
|
/// <returns>the component from the cloned GameObject</returns>
|
|
public static Component DuplicateGameObject(this Component source, Transform newParent, bool keepPrefabConnection = false)
|
|
{
|
|
if (!source || !source.gameObject || !newParent)
|
|
return null;
|
|
|
|
List<Component> cmps = new List<Component>(source.gameObject.GetComponents<Component>());
|
|
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<Component>();
|
|
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
|
|
{
|
|
|
|
/// <summary>
|
|
/// Checks if at least one of the provided flags is set in variable
|
|
/// </summary>
|
|
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<T>(this T value, T flag) where T : struct
|
|
{
|
|
long lValue = Convert.ToInt64(value);
|
|
long lFlag = Convert.ToInt64(flag);
|
|
return (lValue & lFlag) != 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets a flag
|
|
/// </summary>
|
|
public static T Set<T>(this Enum value, T append) { return Set(value, append, true); }
|
|
/// <summary>
|
|
/// Sets a flag
|
|
/// </summary>
|
|
/// <param name="OnOff">whether to set or unset the value</param>
|
|
public static T Set<T>(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);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets x/y
|
|
/// </summary>
|
|
public static Rect SetPosition(this Rect rect, Vector2 pos)
|
|
{
|
|
rect.x = pos.x;
|
|
rect.y = pos.y;
|
|
return new Rect(rect);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets x/y
|
|
/// </summary>
|
|
public static Rect SetPosition(this Rect rect, float x, float y)
|
|
{
|
|
rect.x = x;
|
|
rect.y = y;
|
|
return new Rect(rect);
|
|
}
|
|
|
|
/// <summary>
|
|
/// gets width/height as Vector2
|
|
/// </summary>
|
|
public static Vector2 GetSize(this Rect rect)
|
|
{
|
|
return new Vector2(rect.width, rect.height);
|
|
}
|
|
/// <summary>
|
|
/// Sets width/height
|
|
/// </summary>
|
|
public static Rect SetSize(this Rect rect, Vector2 size)
|
|
{
|
|
rect.width = size.x;
|
|
rect.height = size.y;
|
|
return new Rect(rect);
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Grow/Shrink a rect
|
|
/// </summary>
|
|
public static Rect ScaleBy(this Rect rect, int pixel) { return ScaleBy(rect, pixel, pixel); }
|
|
/// <summary>
|
|
/// Grow/Shrink a rect
|
|
/// </summary>
|
|
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
|
|
{
|
|
/// <summary>
|
|
/// Converts a HTML color endcoded string int a color
|
|
/// </summary>
|
|
/// <param name="hexString">html color of type [#]rrggbb[aa]</param>
|
|
/// <returns>a Color</returns>
|
|
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<T>(this IEnumerable<T> ie, Action<T> action)
|
|
{
|
|
foreach (T i in ie)
|
|
{
|
|
action(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static class ArrayExt
|
|
{
|
|
public static T[] SubArray<T>(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<T>(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<T>(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<T>(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<T>(this T[] source, T item)
|
|
{
|
|
Array.Resize(ref source, source.Length + 1);
|
|
source[source.Length - 1] = item;
|
|
return source;
|
|
}
|
|
|
|
public static T[] AddRange<T>(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<T>(this T[] source)
|
|
{
|
|
List<T> res = new List<T>();
|
|
HashSet<T> hash = new HashSet<T>();
|
|
foreach (T p in source)
|
|
{
|
|
if (hash.Add(p))
|
|
{
|
|
res.Add(p);
|
|
}
|
|
}
|
|
return res.ToArray();
|
|
}
|
|
|
|
public static int IndexOf<T>(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<T>(this T[] source, T item)
|
|
{
|
|
int idx = source.IndexOf<T>(item);
|
|
if (idx > -1)
|
|
return source.RemoveAt<T>(idx);
|
|
else
|
|
return source;
|
|
}
|
|
}
|
|
|
|
public static class MeshFilterExt
|
|
{
|
|
/// <summary>
|
|
/// Returns a shared mesh to work with. If existing, it will be cleared
|
|
/// </summary>
|
|
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
|
|
{
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
#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<Assembly> loadedAssemblies = GetLoadedAssemblies();
|
|
List<Type> types = new List<Type>(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
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
#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<Assembly> 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
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all Types T that have an attribute U
|
|
/// </summary>
|
|
public static Dictionary<U, Type> GetAllTypesWithAttribute<U>(this Type type)
|
|
{
|
|
Dictionary<U, Type> res = new Dictionary<U, Type>();
|
|
|
|
IEnumerable<Type> 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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets all fields of a type that have a certain attribute
|
|
/// </summary>
|
|
public static List<FieldInfo> GetFieldsWithAttribute<T>(this Type type, bool includeInherited = false, bool includePrivate = false) where T : Attribute
|
|
{
|
|
FieldInfo[] flds = type.GetAllFields(includeInherited, includePrivate);
|
|
List<FieldInfo> res = new List<FieldInfo>();
|
|
foreach (FieldInfo fi in flds)
|
|
{
|
|
if (fi.GetCustomAttribute<T>() != null)
|
|
res.Add(fi);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a custom attribute from a type (Crossplatform)
|
|
/// </summary>
|
|
public static T GetCustomAttribute<T>(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;
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a Method (Crossplatform)
|
|
/// </summary>
|
|
/// <param name="type">type containing the method</param>
|
|
/// <param name="name">Name of method</param>
|
|
/// <param name="includeInherited">Whether methods of base classes should be returned as well</param>
|
|
/// <param name="includePrivate">Whether private methods should be returned as well</param>
|
|
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
|
|
}
|
|
/// <summary>
|
|
/// Finds a Field (Crossplatform)
|
|
/// </summary>
|
|
/// <param name="type">type containing the field</param>
|
|
/// <param name="name">Name of field</param>
|
|
/// <param name="includeInherited">Whether fields of base classes should be returned as well</param>
|
|
/// <param name="includePrivate">Whether private fields should be returned as well</param>
|
|
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
|
|
}
|
|
/// <summary>
|
|
/// Finds a Property (Crossplatform)
|
|
/// </summary>
|
|
/// <param name="type">type containing the property</param>
|
|
/// <param name="name">Name of property</param>
|
|
/// <param name="includeInherited">Whether properties of base classes should be returned as well</param>
|
|
/// <param name="includePrivate">Whether private properties should be returned as well</param>
|
|
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
|
|
}
|
|
/// <summary>
|
|
/// Gets all fields (Crossplatform)
|
|
/// </summary>
|
|
/// <param name="type">type to reflect</param>
|
|
/// <param name="includeInherited">Whether fields of base classes should be returned as well</param>
|
|
/// <param name="includePrivate">Whether private fields should be returned as well</param>
|
|
public static FieldInfo[] GetAllFields(this Type type, bool includeInherited = false, bool includePrivate = false)
|
|
{
|
|
#if NETFX_CORE
|
|
|
|
List<FieldInfo> 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<FieldInfo> res = new List<FieldInfo>();
|
|
while (currentType != typeof(object))
|
|
{
|
|
res.AddRange(currentType.GetFields(flags));
|
|
currentType = currentType.BaseType;
|
|
}
|
|
return res.ToArray();
|
|
}
|
|
else
|
|
return type.GetFields(flags);
|
|
#endif
|
|
}
|
|
/// <summary>
|
|
/// Gets all properties (Crossplatform)
|
|
/// </summary>
|
|
/// <param name="type">type to reflect</param>
|
|
/// <param name="includeInherited">Whether properties of base classes should be returned as well</param>
|
|
/// <param name="includePrivate">Whether private properties should be returned as well</param>
|
|
public static PropertyInfo[] GetAllProperties(this Type type, bool includeInherited = false, bool includePrivate = false)
|
|
{
|
|
#if NETFX_CORE
|
|
List<PropertyInfo> 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<PropertyInfo> res = new List<PropertyInfo>();
|
|
while (currentType != typeof(object))
|
|
{
|
|
res.AddRange(currentType.GetProperties(flags));
|
|
currentType = currentType.BaseType;
|
|
}
|
|
return res.ToArray();
|
|
}
|
|
else
|
|
return type.GetProperties(flags);
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Whether the type is a framework type, i.e. a primitive, string or DateTime (Crossplatform)
|
|
/// </summary>
|
|
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
|
|
/// <summary>
|
|
/// Gets a custom attribute (CrossPlatform)
|
|
/// </summary>
|
|
public static T GetCustomAttribute<T>(this FieldInfo field) where T : Attribute
|
|
{
|
|
object[] at = field.GetCustomAttributes(typeof(T), true);
|
|
return (at.Length > 0) ? (T)at[0] : null;
|
|
|
|
}
|
|
#endif
|
|
}
|
|
}
|